【Laravel】リレーションシップでぜひ知ってほしいsync()メソッド

こんにちは❗フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、このブログでは予約投稿をしているので実はこの記事は久しぶりに書いています。

なぜ記事を書くたくなったかと言うと、

「これはもっと早く知っておきたかった😂」

という機能に遭遇したからです。

実はこの機能はLaravel 4.2でも使えるので、もう使ってる人からすると「当たり前じゃん・・・」となるかもしれませんが、先日その便利さを実感したのでぜひ記事としてお届けしたくなりました。

具体的に言うと、リレーションシップの保存に使う、

sync()

です。

このメソッドを使うと、いちいちデータ削除や、存在チェックをせずに連結テーブルのデータ更新ができちゃうスグレモノなんですね👍

そこで❗

今回は省コードを実現できる、このsync()メソッドの使い方をご紹介したいと思います。

ぜひ皆さんのお役に立てると嬉しいです😊✨
(なお、おまけ的なカンジで中間テーブルについても説明しています)

「やっぱりブログ書くの楽しいですね❗」

実行環境: Laravel 7.x

どんな場合に便利??

sync()は、冒頭でも説明したとおりリレーションシップ先のデータを簡単に更新することができる機能です。

例えば、「各ユーザーがどんな趣味(複数OK)をもっているか」をDBで結合している場合を考えてみましょう。

データとしては次のようになります。

  • 太郎さん(趣味: テニス、ゴルフ、ジョギング)
  • 次郎さん(趣味: 陶芸)
  • 三郎さん(趣味: プログラミング、作曲)

では、この前提で太郎さんの趣味をsync()で変更してみましょう。

$user = \App\User::find(1);
$user->hobbies()->sync([1, 2, 3]); // hobbiesのIDが1, 2, 3のものに更新

はい❗たったこれだけで、リレーションシップ先の中身を入れ替えてくれます。(もちろん新規追加の場合もOKです)

では、ここからは実際にどうつくっていくかをみていきましょう。(中間テーブルも理解できるようがんばります😊👍)

ユーザーの趣味を管理する機能をつくる

では、先ほど書いた「各ユーザーがどんな趣味(複数OK)をもっているか」を管理する機能をつくっていきます。

なお、ログイン機能がすでにインストールされ、さらにログインできるユーザーデータが作成されていることが前提です。

まだの方はLaravel6.x以降でログイン機能をインストールする方法を参考にして実行しておいてください。

テーブルは次のようになります。

モデル&マイグレーションをつくる

まずはデータベースまわりから実装していきましょう。
必要なのは、全部で以下の3テーブルです。

  • users: ユーザー(作成済み)
  • hobbies: 趣味
  • user_hobbies: ユーザーと趣味の中間テーブル

では、先にhobbiesからつくっていきます。
以下のコマンドを実行してください。

php artisan make:model Hobby -m

すると、モデルとマイグレーションが一気に作成されるので、マイグレーションの中身を次のようにします。

/database/migrations/****_**_**_******_create_hobbies_table.php

// 省略

public function up()
{
    Schema::create('hobbies', function (Blueprint $table) {
        $table->id();
        $table->string('name')->comment('趣味の名前'); // 👈追加
        $table->timestamps();
    });
}

// 省略

次に中間テーブルのuser_hobbiesです。
同じく以下のコマンドを実行してください。

php artisan make:model UserHobby -m

そして、こちらもマイグレーションを変更します。

/database/migrations/****_**_**_******_create_user_hobbies_table.php

// 省略

public function up()
{
    Schema::create('user_hobbies', function (Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('user_id')->comment('ユーザーID'); // 👈 追加
        $table->unsignedBigInteger('hobby_id')->comment('趣味ID'); // 👈 追加
//      $table->timestamps(); // 不要なのでコメントアウト

        $table->foreign('user_id')->references('id')->on('users'); // 👈 追加
        $table->foreign('hobby_id')->references('id')->on('hobbies'); // 👈 追加
    });
}

// 省略

では、この状態でいったんマイグレーションを実行してみましょう。

php artisan migrate

各テーブルはこうなりました。

テストデータをつくる

では、まだ趣味テーブルにはデータが存在しないのでSeeder機能で以下のテストデータを追加していきましょう。

  • テニス
  • ゴルフ
  • ジョギング
  • 陶芸
  • プログラミング
  • 作曲

以下のコマンドを実行してください。

php artisan make:seed HobbiesTableSeeder

するとファイルが作成されるので中身を次のようにします。

/database/seeds/HobbiesTableSeeder.php

// 省略

public function run()
{
    $hobby_names = [
        'テニス',
        'ゴルフ',
        'ジョギング',
        '陶芸',
        'プログラミング',
        '作曲'
    ];

    foreach ($hobby_names as $hobby_name) {

        $hobby = new \App\Hobby();
        $hobby->name = $hobby_name;
        $hobby->save();

    }
}

// 省略

次に作成したHobbiesTableSeederLaravelに登録しましょう。

/database/seeds/DatabaseSeeder.php

public function run()
{
     // 省略

     $this->call(HobbiesTableSeeder::class); // 👈 追加
}

では、以下のコマンドを実行してDBをリセットします。

php artisan migrate:fresh --seed

すると、テーブルはこうなります。

中間テーブルの考え方

ここで中間テーブルの考え方をできるだけシンプルにご紹介します。
まず図を見てください。

中間テーブルは2つのテーブル(上の例では「ユーザー」と「趣味」)をつなぐためだけに存在しているテーブルです。そのため、1行ごとに2つの各IDを保存しています。

※つまり、中間テーブルは「川に架かった橋」をイメージすると分かりやすいかもしれません。橋が存在していることでお互いをつなぐことができます。

こうすることで、「ユーザー」も「趣味」もすっきりしたテーブル構成で実装できると訳ですね。

そして、さらにこの中間テーブルという「橋」はユーザー側、趣味側どちらからでも通れるので、逆に「趣味」をメインに考えたときは以下のようにしてリレーションシップがつくれます。

つまり、「多対多」の連結ですね。

リレーションシップをつくる

では、先ほど説明した中間テーブルを使って「各ユーザーがどんな趣味(複数OK)をもっているか」のリレーションシップをつくりましょう。

/app/User.php

<?php

// 省略

class User extends Authenticatable
{
    // 省略

    // リレーションシップ
    public function hobbies() { // 👈 追加

        return $this->belongsToMany('App\Hobby', 'App\UserHobby');

    }

さあ、これでsync()が使える環境が整いました❗

テストしてみる

では、実際にテストしてみましょう。
まずは太郎さんに以下3つの趣味をもたせてみます。

  • テニス(ID: 1)
  • ゴルフ(ID: 2)
  • ジョギング(ID: 3)

routes/web.php

// 省略

Route::get('sync', function(){

    $hobby_ids = [
        1, // テニス
        2, // ゴルフ
        3, // ジョギング
    ];

    $user = \App\User::find(1); // 太郎さん
    $user->hobbies()->sync($hobby_ids);

});

※本来はコントローラーに書くべきですがテストなので直接ルートに書いています。

ブラウザから「http://******/sync」にアクセスするとuser_hobbiesは次のようになりました。

数字だけなので分かりにくいですが、太郎さん(user_id: 1)のデータが3つ作成されているのがわかります。

では、リレーションシップが上手くいっているかコードを変更して中身を見てみましょう。

routes/web.php

// 省略

Route::get('sync', function(){

    $user = \App\User::with('hobbies')->find(1);
    dd($user->toArray());

});

ブラウザでアクセスすると・・・

はい❗
太郎さんに「テニス」「ゴルフ」「ジョギング」が連結できています。

ではその後、太郎さんがゴルフをやめてしまって、作曲を趣味としてはじめた場合を考えてみましょう。

つまり、現在の太郎さんの趣味は以下3つになります。

  • テニス(ID: 1)
  • ジョギング(ID: 3)
  • 作曲(ID: 6)

ここがsync()のすごいところなのですが、実はコードはほぼ同じで趣味IDだけを変更するだけでOKです。

// 省略

Route::get('sync', function(){

    $hobby_ids = [ // 👈 ここを変更するだけ
        1, // テニス
        3, // ジョギング
        6, // 作曲
    ];

    $user = \App\User::find(1);
    $user->hobbies()->sync($hobby_ids);

});

では、この状態でブラウザからアクセスした後、user_hobbiesテーブルを見てみます。

はい❗
hobby_idsync()で指定した3つに変更になっています。

※さらにidに注目していただきたいのですが、sync()は一旦すべて削除してデータ追加するのではなく、変更する必要がないものは以前のままになっています。

では、こちらもデータを確認してみましょう。

うまく変更されています。
お疲れ様でした😊✨

おわりに

ということで、今回はLaravelでリレーションシップ先のデータを更新する時にとても便利なsync()メソッドをご紹介しました。

sync()を使えば、いちいち中間テーブルをチェックして、削除して、追加する・・・ようなコードは不要になるので、時短できますし、コードが減るイコール間違いも少なくなるのでメリットは多いんじゃないでしょうか。

ぜひ皆さんも活用してみてくださいね。

ではでは〜❗

「外出自粛が明けたので
やっと髪の毛を切りに行けました(サッパリ!)」

開発のご依頼お待ちしております 😊✨
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly  

開発効率を上げるための機材・まとめ