Обновление отношений «один ко многим» с помощью методов updateOrCreate

#php #laravel #eloquent

#php #laravel #красноречивый

Вопрос:

Моя основная модель Tag имеет отношение «один ко многим», Product где одному Tag может быть много Products назначенных им через tag_id связь в БД.

В моем режиме редактирования я разрешаю пользователям редактировать тег products . Эти продукты могут быть добавлены / отредактированы / удалены по запросу формы.

Каждое product поле в форме выбирается из request() массива. Например: request('title'),request('price') .

Например, я установил $title[$key] как request('title') массив.

Затем я подумал о том, чтобы перебрать каждый из products для этого tag и updateOrCreate на основе данных запроса. Проблема здесь в том, что нет способа определить, действительно ли эта конкретная product нуждалась в обновлении.

TagController — Обновление модели продукта («Один ко многим»)

 foreach($tag->products as $key => $product){

  Product::updateOrCreate([
   'id'  => $product->id,
   ],
     [
       'title' => $title[$key],
       'price' => $price[$key],
       'detail' => $detail[$key],
       'order' => $order[$key],
       'tagX' => $tagX[$key],
       'tagY' => $tagY[$key],
       'thumb' => $img[$key],
   ]);
}
  

Для первоначального tag обновления я установил if инструкцию, которая отлично работает (хотя и неаккуратно) для основного тега img .

TagController — Обновить модель тега

 //Update the tag collection
if($request->hasFile('img')) {
  $tag->update([
    'name' => $name,
    'hashtag' => $hashtag,
    'img' => $imgPub,
  ]);
} else{
  $tag->update([
    'name' => $name,
    'hashtag' => $hashtag,
  ]);
}
  

Есть ли лучший способ определить, были ли product поля обновлены по запросу?

В идеале я хотел бы, чтобы пользователь мог добавлять / удалять или редактировать products из tag запроса на редактирование, но не удалять существующие изображения продукта, если они не были обновлены. Спасибо!

Комментарии:

1. Рассматривали ли вы возможность добавления флага (столбца) к объекту product. Итак, этот флаг устанавливается после обновления продукта и инициализируется обратно, как только вы выполняете нужное действие, когда продукт был обновлен?

Ответ №1:

На мой взгляд, вы на неверном пути. Отношения тегов должны быть многие ко многим.

Вы должны проверить sync() метод Laravel в отношениях ManyToMany. Пожалуйста, проверьте это: https://laravel.com/docs/5.8/eloquent-relationships#many-to-many

Примечания: Пожалуйста, добавляйте коды в соответствии с вашей бизнес-логикой.

Product.php

   public function products(){
    return $this->belongsToMany( 'AppProduct', 'product_tags', 'tag_id', 'product_id');
  }
  

Tag.php

    public function tags(){
     return $this->belongsToMany( 'AppTag', 'product_tags', 'product_id', 'tag_id');
  }
  

ProductController.php

   public function update(Product $products,  Request $request){
    //update the product
    $products->update($request->all());

    //attach new tags to the product 
    $products->tags()->sync($request->input('tag_list'));

    return redirect('articles');
 }
  

Комментарии:

1. Я не уверен, что это правильный подход. Одному может быть несколько, products назначенных одному, tag но products не может быть назначено более одного tag .

2. Я решил использовать many to many то, что предложил @mayank-dudakiya . Я использовал detach then ->sync() для присвоения сводной таблице. Я позволю пользователям обновлять products из отдельного представления, чтобы все было чище.

3. @scopeak Просто для упоминания eloquent предоставляет функцию syncWithoutDetach(), если вы хотите только присоединять новые продукты, но не удалять существующие!

Ответ №2:

Обычно я делаю что-то подобное на стороне интерфейса

 <div class="product">
   <!-- For Products that may be edited -->
   <input type="hidden" name="id" value={{$product->id??0}}>
   <input type="hidden" name="action" value="update">
   <input type="text" name="title" value={{$product->title}}>
      ...
</div>
<div class="product">
   <!-- For New Products -->
   <input type="hidden" name="id" value=0>
   <input type="hidden" name="action" value="add">
   <input type="text" name="title" value="New Title">
      ...
</div>
<div class="product">
   <!-- For Deleted Products -->
   <input type="hidden" name="id" value={{$product->id}}>
   <input type="hidden" name="action" value="delete">
   <input type="text" name="title" value="New Title">
      ...
</div>
  

Создайте полезную нагрузку json (массив объектов продукта) с помощью javascript и отправьте ajax-запрос на серверную часть

 [{'id':101,'title':'product 1','action':'update'},
{'id':102,'title':'product 2','action':'update'},
... , 
{'id':201,'action':'delete'}, 
{'id':202, 'action':'delete'},
... ,
{'id':0,'title':'new product 1','action':'add'}, 
{'id':0,'title':'new product 2','action':'add'}]
  

Теперь на стороне серверной части просмотрите список продуктов и проверьте наличие действий. В зависимости от действия, выполните необходимый шаг.