LaravelのEloquent ORMはリレーションを非常に簡単に扱えますが、「1対多」や「多対多」の関連データをフォームから受け取って登録・更新する処理は、意外と面倒に感じることがあります。特に、チェックボックスで選択されたカテゴリIDを中間テーブルに保存するようなケースです。
「既存の関連を全部消して、新しいのを登録し直す?」「いや、差分だけ計算して追加・削除?」
そんな悩みを一発で解決してくれるのが、今回ご紹介する sync() メソッドです。この記事では、SEOキーワード「Laravel 1対多 更新」で検索してたどり着いたあなたにも、sync() の圧倒的な利便性をお伝えします。
「Laravel 1対多 更新」で検索されたかもしれませんが、sync() メソッドが本領を発揮するのは、実は「多対多 (belongsToMany)」リレーションシップです。
`sync()` メソッドはなぜこんなに便利なのか?
結論から言うと、sync() は「中間テーブルの状態を、渡した配列と全く同じ状態に同期してくれる」からです。例えば、ある商品 (ID: 1) に関連するカテゴリIDを [1, 3, 5] にしたい場合を考えます。
従来の面倒な処理
sync() を使わない場合、以下のような複雑な処理が必要でした。
// 1. フォームから新しいカテゴリIDの配列を取得
$newCategoryIds = $request->input('category_list', []); // 例: [1, 3, 5]
// 2. 現在の商品を取得
$product = Product::find(1);
// 3. 現在関連付いているカテゴリIDを取得
$currentCategoryIds = $product->categories()->pluck('id')->toArray(); // 例: [1, 2, 4]
// 4. 削除すべきIDを計算 (現在にあって、新しいにない)
$detachIds = array_diff($currentCategoryIds, $newCategoryIds); // [2, 4]
// 5. 追加すべきIDを計算 (新しいにあって、現在にない)
$attachIds = array_diff($newCategoryIds, $currentCategoryIds); // [3, 5]
// 6. 削除実行
if (!empty($detachIds)) {
$product->categories()->detach($detachIds);
}
// 7. 追加実行
if (!empty($attachIds)) {
$product->categories()->attach($attachIds);
}
差分を計算して、detach() (削除) と attach() (追加) を呼び分ける…。非常に面倒ですね。
`sync()` を使った場合の処理
これが sync() を使うと、たった1行になります。魔法の1行:
$product = Product::find(1);
$newCategoryIds = $request->input('category_list', []); // 例: [1, 3, 5]
// これだけ!
$product->categories()->sync($newCategoryIds);
この1行で、Laravelは裏側で以下の処理を自動的に実行してくれます。
- 渡された配列 [1, 3, 5] に含まれていない既存の関連 (例: ID 2, 4) を中間テーブルから削除 (detach) します。
- 渡された配列 [1, 3, 5] のうち、まだ中間テーブルに存在しない関連 (例: ID 3, 5) を追加 (attach) します。
- 渡された配列 [1, 3, 5] のうち、すでに存在する関連 (例: ID 1) はそのまま保持します。
詳しくはこちら>>