Laravelがピボットテーブルに複数のレコードを追加しないようにする


97

私が使用するカートにアイテムを追加するために、多対多の関係が設定されて機能しています。

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

(必要に応じて)ピボットテーブルにアイテムを追加しますが、ユーザーがリンクを再度クリックして既に追加したアイテムを追加すると、ピボットテーブルに重複したエントリが作成されます。

レコードがまだ存在しない場合にのみ、ピボットテーブルにレコードを追加する組み込みの方法はありますか?

そうでない場合、ピボットテーブルをチェックして、一致するレコードが既に存在するかどうかを確認するにはどうすればよいですか?

回答:


76

次のような非常に単純な条件を記述することにより、既存のレコードの存在を確認できます。

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

または、データベースに単一性条件を追加できます。ダブレットを保存しようとすると例外がスローされます。

また、すぐ下のBarryvdhからのより簡単な答えも見てください。


1
方法$ IDパラメータがattach()混合され、それはモデルのintまたはインスタンスとすることができる。) -参照github.com/laravel/framework/blob/master/src/Illuminate/...
ロブGordijn

@RobGordijnありがとうございます。今日は何かを学びました!回答を編集しました。
Alexandre Butynski 2013

1
@bagusflyer containsステートメントは、キーがコレクション内の1つのオブジェクトに存在するかどうかを確認します。コードに間違いがあるはずです。
Alexandre Butynski 2014年

4
このソリューションは好きではありません。余分なクエリ($ cart-> items)を強制するからです。次のような $cart->items()->where('foreign_key', $foreignKey)->count() ことをしています。本当に必要でない限り、コレクション全体に水分を補給します。
沈黙

1
そうです、あなたのソリューションはもう少し最適化されています。最適化のexists()代わりに関数を使用することもできcount()ます。
Alexandre Butynski、2015年

264

$model->sync(array $ids, $detaching = true)メソッドを使用して、デタッチを無効にすることもできます(2番目のパラメーター)。

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

更新:Laravel 5.3または5.2.44以降、syncWithoutDetachingを呼び出すこともできます。

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

まったく同じですが、より読みやすくなっています:)


2
リストされていないIDのデタッチを防ぐための2番目のブールパラメータは、メインのL5ドキュメントにはありません。知っておくととても良いです-単一のステートメントで「まだアタッチされていない場合はアタッチする」唯一の方法のようです
Jason

11
これは答えとして受け入れられるべきであり、受け入れられた答えよりもはるかに優れた方法です
Daniel

4
私のような初心者の場合:$cart->items()->sync([1, 2, 3])指定した配列内のIDのへの多対多の関係を構築します、12、及び3、削除(または「デタッチ」)他のすべてのIDは配列ではありません。この方法では、配列で指定されたIDのみがテーブルに存在します。Brilliant @Barryvdhは、文書化されていない2番目のパラメーターを使用してその切り離しを無効にするため、指定された配列にない関係は削除されず、一意のIDのみがアタッチされます。「同期のための同期」ドキュメントを確認してください。(Laravel 5.2)
identicon

3
参考までに、ドキュメントを5.2以上に更新しました:laravel.com/docs/5.2/…そして、テイラーはすぐにaddを追加しsyncWithoutDetaching()、2番目のパラメーターとしてfalseを指定してsync()を呼び出します。
Barryvdh

Laravel 5.5laravel.com/docs/5.5/… syncWithoutDetaching()機能しました!
Josh LaMar 2017

2

@alexandre Butynskyメソッドは非常にうまく機能しますが、2つのSQLクエリを使用します。

カートに商品が含まれているかどうかを確認するものと、保存するものです。

1つのクエリのみを使用するには、これを使用します。

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

これが私のプロジェクトでやったことです。しかし、問題は、この例外が重複したエントリなどによって引き起こされているのかどうか分からないことです。
Bagusflyer 2014年

2
なぜ例外が発生するのですか?いくつかのユニークなキーがあるかどうそれは次のようになります
誠司Manoan

1

これらすべての回答と同じくらい良いのは、私がそれらすべてを試したためです。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

そのまま使えます

$cart->items()->sync($items)

Laravel 5.7以降:

関連の同期syncメソッドを使用して、多対多の関連を構築することもできます。syncメソッドは、中間テーブルに配置するIDの配列を受け入れます。指定された配列にないIDは、中間テーブルから削除されます。したがって、この操作が完了すると、指定された配列のIDのみが中間テーブルに存在します。


これが最良の解決策です
eifersucht

これは、重複する行を回避しながら単一のリレーションを追加する方法であるという質問にも答えません。
hackel

同期するとアイテムが追加され、重複する行が作成されることはなくなります。
Shreyansh Panchal

@hackelのコメントを完了するために、カートから他のすべてのアイテムも削除されます。
chrissharkman

0

すでにいくつかの素晴らしい回答が投稿されています。これもここに捨てたかった。

@AlexandreButynskiと@Barryvdhからの回答は私の提案よりも読みやすく、この回答が追加するのはある程度の効率です。

現在の組み合わせ(実際にはIDのみ)のエントリのみを取得し、存在しない場合は添付します。syncメソッドは(デタッチなしでも)、現在アタッチされているすべてのIDを取得します。イテレーションが少ない小さなセットの場合、これはほとんど違いがありません。

とにかく、それは間違いなく読みやすくはありませんが、トリックを行います。

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.