Laravelは多対多リレーションを使って、データを色々な形で保存できます。
意外と便利なのが、syncWithoutDetaching。
完全重複以外は、重複OKにできちゃいます。
syncWithoutDetachingを直訳すると「デタッチ(紐づけ解除)せずに、シンク(同期)をする」という意味。
他の方法との比較と、使い方を解説していきますね。
attach, sync, syncWithoutDetachingの比較と使い方
【リレーションでデータ保存する方法比較】
| attach | 中間テーブルにデータを挿入できる
 重複はいくらでもOK  | 
|---|---|
| sync | 中間テーブルに【値を重複させず】データを挿入できる
 重複した場合は削除  | 
| syncWithoutDetaching | 中間テーブルに【値を重複させず】データを挿入できる
 完全重複以外は、重複OK  | 
なお、どの方法を使うにしても最初にふたつのモデル同士でリレーションを作っておきましょう。
今回はQuizモデルとUserモデルでリレーションを作ります。
前準備
まずは前準備をしておきます。
すでにリレーションができている方は、この項目はスキップしてください。
①リレーション作り
Quizモデルファイルに、次のように入れておきます。
| 
					 1 2 3  | 
						    public function users(){         return $this->belongsToMany(App\Models\User)->withTimestamps();     }  | 
					
Userモデルファイルには、次のように入れます。
| 
					 1 2 3  | 
						    public function quizes(){         return $this->belongsToMany('\App\Models\User')->withTimestamps();     }  | 
					
リレーションの作り方は、下記の記事でモデルの作り方から解説しています。
②ルート設定
ルート設定を作成します。
メソッドは 【put】としておきます。
| 
					 1  | 
						    Route::put('/quiz/{quiz}/completed', 'HomeController@completed')->name('quiz.completed');  | 
					
アドレスなどはお好きなように設定してください。
③ビューファイル(blade.php)作成
ビュー部分を作っておきます。
今回は、ボタンを押すと、コントローラーで処理が行われるようにします。
blade.phpファイルに次のようなボタンを追加しておきましょう。

| 
					 1 2 3 4 5  | 
						<form method="post" action="{{ route('quiz.completed', $quiz) }}">     @method('put')     @csrf     <button type="submit" class="btn btn-danger w-50">クイズ完了</button> </form>  | 
					
attachを使った場合
それでは、コントローラー側の処理を記述していきましょう。
まずはattachから。
フォームからデータを受け取った後、コントローラーのコードは、こんなふうになります。
| 
					 1 2 3 4  | 
						$quizId=$inputs['quiz_id']; $userId=$inputs['user_id']; $quiz=Quiz::find($quizId); $quiz->users()->attach($userId);  | 
					
attachを使うと、完全重複を含め、すべてのデータが中間テーブルに保存されます。

買い物かごに商品を追加する時などには、使えます。
ただ、完全に同じデータは保存不要、という場合には向きません。
syncを使った場合
次はsync。syncは重複なしで登録したい場合に使います。
コントローラーの記述はこんなふうにします。
| 
					 1 2 3 4  | 
						$quizId=$inputs['quiz_id']; $userId=$inputs['user_id'];  $quiz=Quiz::find($quizId);         $quiz->users()->sync($userId);  | 
					
この状態でデータを保存していくとしましょう。
例えば、次のようなデータがあるとします。

ここで、quiz_id=4, user_id=2のデータを保存してみます。
すると次のように、quiz_id=4, user_id=4 のデータが削除されてしまいました。

syncはこのように重複を消してくれます。
ただ、自動的に【削除=紐づけ解除】してしまうので、注意してください。
勝手に削除したくない場合は、次のように引数にfalseを入れます。
| 
					 1  | 
						        $quiz->users()->sync($userId, false);  | 
					
ただ、なんだか怖いですし処理がメンドウ。
そこで、便利なのが次のsyncWithoutDetachingです。
syncWithoutDetachingを使った場合
syncWithoutDetachingはその名のとおり、デタッチ(紐づけ解除)なしでsyncができるメソッド。
コード例はこちら。
| 
					 1 2 3 4  | 
						$quizId=$inputs['quiz_id'];  $userId=$inputs['user_id'];  $quiz=Quiz::find($quizId); $quiz->users()->syncWithoutDetaching($userId);  | 
					
次のように、完全重複以外は、重複OK。
.jpg)
ケースバイケースですが、syncWithoutDetachingは活躍の場が多いような気がします。
ただ名前が長すぎるせいか^^; あまり知られていない気も。
機会があったら、お試しください。
  
  
  
  

