web-dev-qa-db-ja.com

Laravel=ピボットテーブルに複数のレコードを追加することを防ぐ

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

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

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

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

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

84
Al_

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

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

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

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

70

$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]);

これはまったく同じですが、より読みやすいです:)

234
Barryvdh

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

カートにアイテムが含まれているかどうかを確認するものと、保存するものがあります。

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

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

このすべての答えは、私がそれらをすべて試したため良いのですが、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);
}
1
adeguk Loggcity

あなただけを使用することができます

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

Laravel 5.7:

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

1

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

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

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

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

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
0
Peter de Kok