Запобігання додаванню Laravel кількох записів у зведену таблицю


98

У мене налаштовані та працюють стосунки багато-до-багатьох, щоб додати товар у кошик, який я використовую:

$cart->items()->attach($item);

Що додає елемент до зведеної таблиці (як і слід), але якщо користувач ще раз натискає посилання, щоб додати елемент, який він уже додав, створюється повторюваний запис у зведеній таблиці.

Чи існує вбудований спосіб додати запис до зведеній таблиці, лише якщо такий ще не існує?

Якщо ні, як я можу перевірити зведену таблицю, щоб знайти, чи вже існує відповідний запис?

Відповіді:


76

Ви можете перевірити наявність існуючого запису, написавши дуже просту умову, як ця:

if (! $cart->items->contains($newItem->id)) {
    $cart->items()->save($newItem);
}

Або / і ви можете додати умову unicity у свою базу даних, це викличе виняток під час спроби збереження дублету.

Ви також повинні поглянути на більш пряму відповідь від Barryvdh трохи нижче.


1
Параметр $ id для методу attach()змішаний, це може бути int або екземпляр моделі;) - див. Github.com/laravel/framework/blob/master/src/Illuminate/…
Роб Гордійн

Дякую @RobGordijn. Я сьогодні чогось навчився! Відповідь відредаговано.
Олександр Бутинський

1
Оператор @bagusflyer containsперевіряє, чи присутній ключ в одному об’єкті в колекції. У вашому коді повинна бути помилка.
Олександр Бутинський

4
Мені не подобається це рішення, оскільки воно змушує зайвий запит ($ cart-> items) я робив щось на зразок: $cart->items()->where('foreign_key', $foreignKey)->count() Яке, ну, насправді теж виконує додатковий запит '^^ Але мені не потрібно отримувати та зволожувати всю колекцію, якщо мені це дійсно не потрібно.
Тиша

1
Правильно, ваше рішення трохи оптимізованіше. Ви навіть можете використовувати exists()функцію замість count()для найкращої оптимізації.
Олександр Бутинський

265

Ви також можете використовувати $model->sync(array $ids, $detaching = true)метод і вимкнути від'єднання (другий параметр).

$cart->items()->sync([$item->id], false);

Оновлення: Починаючи з Laravel 5.3 або 5.2.44, ви також можете зателефонувати syncWithoutDetaching:

$cart->items()->syncWithoutDetaching([$item->id]);

Що робить точно те саме, але читабельніше :)


2
Другий логічний параметр для запобігання від'єднанню перелічених ідентифікаторів відсутній у основній документації L5. Це дуже добре знати - здається, це єдиний спосіб "прикріпити, якщо його вже не долучити" в одному твердженні.
Джейсон

11
Це слід прийняти як відповідь, це набагато кращий спосіб зробити це, ніж прийняту відповідь
Даніель

4
Для новачків на зразок мене: $cart->items()->sync([1, 2, 3])будуватимуть багато-до-багатьох до ідентифікаторів в даному масиві 1, 2і 3, і видалення (або «відкріплення») всі інші ідентифікатори не в масиві. Таким чином, у таблиці будуть існувати лише ідентифікатори, вказані в масиві. Brilliant @Barryvdh використовує недокументований другий параметр, щоб вимкнути це від'єднання, тому жодні зв'язки, що не містяться в даному масиві, не видаляються, а будуть додані лише унікальні ідентифікатори. Перегляньте документ "Синхронізація для зручності". (Laravel 5.2)
ідентифікація

3
FYI, я оновив документи, так 5.2 і вище: laravel.com/docs/5.2/… І Тейлор відразу додав add syncWithoutDetaching(), який викликає sync () з false як другий параметр.
Barryvdh

Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching() спрацював!
Джош Ламар

2

Метод @alexandre Butynsky працює дуже добре, але використовує два запити sql.

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

Щоб використовувати лише один запит, використовуйте це:

try {
    $cart->items()->save($newItem);
}
catch(\Exception $e) {}

Це те, що я зробив у своєму проекті. Але проблема полягає в тому, що ви не знаєте, чи цей виняток спричинений дубльованим записом або чимось іншим.
Bagusflyer

2
навіщо це робити якийсь виняток? це було б, якщо б там був якийсь унікальний ключ
Сейдзі Маноан

1

Наскільки хороші всі ці відповіді, тому що я їх усі спробував, одне все ще залишається без відповіді або не вирішено: проблема оновлення попередньо перевіреного значення (знято прапорець). У мене є щось подібне до вищезазначеного питання, я очікую, що я хочу перевірити та зняти прапорці щодо продуктів у моїй таблиці характеристик продукту (зведеній таблиці). Я новачок, і я зрозумів, що ніхто з перерахованого вище цього не робив. Обидва вони хороші при додаванні нових функцій, але не тоді, коли я хочу видалити існуючі функції (тобто зняти його)

Я буду вдячний за будь-яке прозріння в цьому.

$features = $request->get('features');

if (isset($features) && Count($features)>0){
    foreach ($features as $feature_id){
        $feature = Feature::whereId($feature_id)->first();
        $product->updateFeatures($feature);
    }
}

//product.php (extract)
public function updateFeatures($feature) {
        return $this->features()->sync($feature, false);
}

або

public function updateFeatures($feature) {
   if (! $this->features->contains($features))
        return $this->features()->attach($feature);
}
//where my attach() is:
public function addFeatures($feature) {
        return $this->features()->attach($feature);
}

Вибачте, хлопці, не впевнений, що мені слід видалити запитання, тому що, коли я сам розгадав відповідь, це звучить трохи по-дурному, ну відповідь на вищесказане є такою ж простою, як робота з @Barryvdh sync () наступним чином; прочитавши все більше і більше про:

$features = $request->get('features');
if (isset($features) && Count($features)>0){
    $product->features()->sync($features);
}

0

Уже є декілька чудових відповідей. Я хотів викинути і цього сюди, хоча.

Відповіді від @AlexandreButynski та @Barryvdh є більш читабельними, ніж моя пропозиція. Що додає ця відповідь, це певна ефективність.

Він отримує лише записи для поточної комбінації (насправді лише ідентифікатор), а потім додає їх, якщо вона не існує. Метод синхронізації (навіть без від'єднання) отримує всі приєднані на даний момент ідентифікатори. Для менших наборів з невеликими ітераціями це навряд чи буде різницею, ... ви розумієте мою думку.

У будь-якому випадку, це точно не так читабельно, але це трюк.

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.