syncで生成されるSQL

参考

多対多

概要

Eloquentのメソッドにsyncというのがある。

これはbelongsToManyの定義されている多対多のリレーション下で使うことができる。

これによって要素同士の関連を定義することができる。つまり自動で中間テーブルを作ることができる。

$team->users()->sync( $user_ids, false );

第一引数は配列で渡す。第二引数にfalseを渡さない場合は第一引数で渡した配列以外の中間テーブルのレコードが削除されてしまうので注意。

またこれによってinsertされるレコードにはcreate_atとupdated_atが付与されない。必要な場合はモデルのリレーションを定義しているメソッドの返り値にwithTimestamps()をつける。

    public function users()
    {
        return $this->belongsToMany(User::class)->withTimestamps();
    }

利点

単に指定された要素同士のリレーションのレコードを作るだけではなく、既に中間テーブルに同じ要素同士のレコードがあり、リレーションがある場合には新たなレコードの作成を中止してくれる。

クエリ

生成されるクエリを確認してみる

$user_ids = [868, 911];
DB::enableQueryLog();
$team->users()->sync( $user_ids, false );
dd(DB::getQueryLog());

sql

array:3 [
  0 => array:3 [
    "query" => "select `user_id` from `team_user` where `team_id` = ?"
    "bindings" => array:1 [
      0 => 15
    ]
    "time" => 1.0
  ]
  1 => array:3 [
    "query" => "insert into `team_user` (`created_at`, `team_id`, `updated_at`, `user_id`) values (?, ?, ?, ?)"
    "bindings" => array:4 [
      0 => Carbon {#898
        +"date": "2019-03-24 12:10:02.000000"
        +"timezone_type": 3
        +"timezone": "Asia/Tokyo"
      }
      1 => 15
      2 => Carbon {#898}
      3 => 868
    ]
    "time" => 1.02
  ]
  2 => array:3 [
    "query" => "insert into `team_user` (`created_at`, `team_id`, `updated_at`, `user_id`) values (?, ?, ?, ?)"
    "bindings" => array:4 [
      0 => Carbon {#899
        +"date": "2019-03-24 12:10:02.000000"
        +"timezone_type": 3
        +"timezone": "Asia/Tokyo"
      }
      1 => 15
      2 => Carbon {#899}
      3 => 911
    ]
    "time" => 0.9
  ]
]

まず、中間テーブルの既存のレコードでダブりのチェックをするためのsqlが走る。

そのあと、配列で渡す要素分の本数のinsertクエリが走ることになる。

注意点

配列で渡す要素の数がわからない場合や、ここで言う$teamが複数あってforeachなどで繰り返し処理を行いたい場合などはクエリが予期しない本数走る場合があるので避けるべき