Предотвращение добавления Laravel нескольких записей в сводную таблицу


100

У меня установлено и работает множество отношений, чтобы добавить товар в корзину, которую я использую:

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

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

Есть ли встроенный способ добавления записи в сводную таблицу, только если она еще не существует?

Если нет, как я могу проверить сводную таблицу, чтобы определить, существует ли уже соответствующая запись?

Ответы:


78

Вы можете проверить наличие существующей записи, написав очень простое условие вроде этого:

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

Или / и вы можете добавить условие уникальности в свою базу данных, это вызовет исключение при попытке сохранить дублет.

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


1
Параметр $ id для метода attach()смешанный, это может быть int или экземпляр модели;) - см. Github.com/laravel/framework/blob/master/src/Illuminate/…
Rob Gordijn

Спасибо @RobGordijn. Я кое-что узнал сегодня! Ответ отредактирован.
Александр Бутынски 05

1
Оператор @bagusflyer containsпроверяет, присутствует ли ключ в одном объекте в коллекции. У вас должна быть ошибка в вашем коде.
Александр Бутынски

5
Мне не нравится это решение, потому что оно вызывает дополнительный запрос ($ cart-> items). Я делал что-то вроде: $cart->items()->where('foreign_key', $foreignKey)->count() Что, ну, на самом деле, тоже выполняет дополнительный запрос '^^ Но мне не нужно получать и гидратировать всю коллекцию, если она мне действительно не нужна.
Тишина,

2
Правильно, ваше решение немного более оптимизировано. Вы даже можете использовать exists()функцию вместо count()для лучшей оптимизации.
Александр Бутынски

267

Вы также можете использовать $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)
identityicon 02

3
К вашему сведению, я обновил документы, поэтому 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

Как бы хороши ни были все эти ответы, потому что я перепробовал их все, одна вещь все еще осталась без ответа или не решена: проблема обновления ранее отмеченного значения (снята отметка с флажком [es]). У меня есть что-то похожее на приведенный выше вопрос, ожидаю, что я хочу проверять и снимать флажки функций продуктов в моей таблице функций продукта (сводная таблица). Я новичок и понял, что ничего из вышеперечисленного не помогло. Оба они хороши при добавлении новых функций, но не тогда, когда я хочу удалить существующие функции (т.е. снять флажок).

Я буду признателен за любое разъяснение по этому поводу.

$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.