【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テーブルのデータが追加されている、コレクションの入れ子を取得できます。