【Laravel】多対多のリレーションをモデルで定義する例|中間テーブルの作成など

今回は、LaravelのEloquentモデルを利用し、テーブルの多対多のリレーションを定義する例となります。

多対多のリレーションを定義する例

多対多のリレーションは、例えば、ブログの「投稿」と「タグ」の関係のように、「投稿」が複数の「タグ」を持つ場合があり、反対に「タグ」が複数の「投稿」を持つ場合がある関係などで利用します。

多対多のリレーションでは、2つのテーブルの関係を表す中間テーブルが必要となります。

今回の例では、以下の3つのテーブルをデータベースに追加し、Eloquentモデルで多対多のリレーションを定義します。

  • posts
  • tags
  • post_tag(中間テーブル)

また、Laravelでの中間テーブルの名前は、リレーションを行うテーブルのモデル名を、アルファベット順に_(アンダースコア)でつなげます。

マイグレーションファイルを作成

まずは、上記の3つのテーブルを追加するマイグレーションファイルを作成します。

テーブルを追加するマイグレーションファイルは、次のコマンドでdatabase/migrations内に作成されます。

php artisan make:migration create_テーブル名_table

マイグレーションファイルについて

上記コマンドで、マイグレーションファイルを作成すると、以下のように、マイグレーション時に、idカラム(主キー)とタイムスタンプのカラムを追加するファイルが作成されます。

テーブルにその他のカラムを追加する方法など、基本的なマイグレーションファイルの利用方法については、以下のリンク先を参考にしてみてください。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {  
            $table->bigIncrements('id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

中間テーブルのマイグレーションファイルの例

多対多のリレーションでは、2つのテーブルの関係を表すため、中間テーブルに複合主キーを定義します。

例ではその複合主キーを、それぞれpostsとtagsテーブルの主キーの外部キーとします。それにより、中間テーブルにpostsとtagsテーブルの関係性を表すデータを保存できます。

以下、中間テーブルのマイグレーションファイルの例となり、マイグレーション時にテーブルを定義するupメソッドを修正します。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostTagTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_tag', function (Blueprint $table) {
            // カラムを追加
            $table->unsignedBigInteger('post_id');
            $table->unsignedBigInteger('tag_id');
            // 複合主キーを定義
            $table->primary(['post_id','tag_id']);
            // 指定したカラムに外部キー制約を定義
            $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
            $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('post_tag');
    }
}

upメソッド内では次のようにテーブルを定義しています。

中間テーブルのカラムにpost_idとtag_idを追加し、複合主キーとして定義します。そして、それぞれの複合主キーには、postsとtagsテーブルのそれぞれのidカラムの主キーのみをセットできるように、外部キー制約を定義します。

onDeleteメソッドでは引数にcascadeを指定することで、外部キーの参照しているキーが削除された場合に、外部キーも自動で削除し、外部キーと参照しているキーとの整合性を保ちます。

また、外部キーのカラムは、参照しているカラムと同じ型である必要があります。

マイグレーションを実行

上記マイグレーションファイルでテーブルを作成後、以下のコマンドでマイグレーションを行うことで、テーブルを追加します。

php artisan migrate

モデルを作成

次に、postsとtagsテーブルのPostとTagモデルを作成します。

モデルのファイルは、次のコマンドでappディレクトリ直下に作成されます。

php artisan make:model ファイル名

また、Laravelでは、テーブル名を複数形としモデル名(ファイル名)を単数形する命名規則があります。それにより、テーブルとモデルが 紐付きます。

モデルに多対多のリレーションを定義

作成したモデルにリレーションを定義します。それにより、PostモデルからTagモデルへアクセスでき、反対にTagモデルからPostモデルへアクセスできます。

モデルにリレーションを定義するには、それぞれのモデルクラス内で新しくメソッドを追加します。多対多のリレーションの場合、作成したメソッド内で、belongsToManyメソッドを利用して定義します。

Postモデルの例

以下、Postモデルクラス内に、tagメソッドを追加した例となります。多対多のリレーションを定義するbelongsToManyメソッドの引数には、Tagモデルを指定します。

それにより、Postモデルからリレーション先のTagモデルを扱えます。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // 多対多のリレーションを定義
    public function tag()
    {
        return $this->belongsToMany('App\Tag');
    }
}

Tagモデルの例

以下、Tagモデルクラス内に、postメソッドを追加した例となります。多対多のリレーションを定義するbelongsToManyメソッドの引数には、Postモデルを指定します。

それにより、Tagモデルからリレーション先のPostモデルを扱えます。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    // 多対多のリレーションを定義
    public function post()
    {
        return $this->belongsToMany('App\Post');
    }
}

動的プロパティを利用しリレーション先の情報を取得

上記のように、モデル内でメソッドを作成しリレーションを定義すると、動的プロパティを利用しリレーション先のデータを取得できます。

また、動的プロパティの名前は、モデルでリレーションを定義したメソッドの名前となります。

例えば、上記Postモデルから、postsテーブルとtagsテーブルのデータを取得する場合、以下のコードとなります。

$result = App\Post::with('tag')->get();

上記では、Postモデルに対して、withメソッドの引数に動的プロパティのtagを指定し、次にgetメソッドを指定しています。

それにより、postsテーブルのそれぞれのデータ内に、tagsテーブルのデータが追加されている、コレクションの入れ子を取得できます。

参考サイトなど

コメント投稿コメント投稿欄を開く

コメントは項目欄(*は必須項目)を入力し、「コメントを送信」ボタンをクリックしてください。 (メールアドレスは公開されることはありません。コメントの公開は承認制となります。)

また、多忙によりコメントには返信できない場合があります。

Twitterで返信する場合はこちらから。