【Laravel】Eloquentのリレーション④ belongsToManyによる多対多の関係
PHPバージョン:7.1.14
Laravelバージョン:5.7.9
今回はLaravelのEloquentリレーション、
多対多についてです!
belongsToManyというメソッドを使います!
Roleモデルを作成
まずはRoleモデルを作成します!
admin(管理者)・編集者(editor)・閲覧者(visitor)
の権限を管理するモデルです。
Author対Roleが多対多となるように作成していきます。
$ php artisan make:model Models/Role
app>Modelsディレクトリに、
Role.phpモデルが作成されました。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Role extends Model { protected $guarded = array('id'); public function authors() { return $this->belongsToMany('App\Models\Author', 'author_role', 'role_id', 'author_id')->withTimeStamps(); } }
app>Models>Role.php
authorsメソッドを作成し、
belongsToManyメソッドでAuthorと多対多の関係構築します。
belongsToManyの引数は以下のように設定します。
第1引数:参照先テーブルのモデルのパス
第2引数:中間テーブル(SQL)のテーブル名
(省略時アルファベット順テーブル名を並べる。例:author_role)
第3引数:中間テーブルの参照元外部キー
(今回でいうrole_id。省略時"参照元モデル名_id")。
第4引数:中間テーブルの参照先外部キー
(今回でいうauthor_id。省略時"参照先モデル名_id"。)
最後にwithTimeStamp()をチェーンすることで、
更新時にupdated_atカラムが更新されます。
roleテーブルのMigration
続いてroleテーブルのMigrationを実行していきます。
$ php artisan make:migration create_roles_table
作成されたMigrationファイルを編集します。
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateRolesTable extends Migration { public function up() { Schema::create('roles', function (Blueprint $table) { $table->increments('id'); $table->string('name')->unique(); $table->timestamps(); }); } public function down() { Schema::dropIfExists('roles'); } }
database>migrations>xxxx_xx_xx_xxxxxx_create_roles_table.php
デフォルトからstring型のnameカラムを追加しました。
またユニークキー制約を付けました。(重複対策)
ついでにSeedingにより、rolesテーブルにデータを登録していきましょう。
$ php artisan make:seeder RolesSeeder
Seedingファイルを作成し、
<?php use Illuminate\Database\Seeder; class RolesSeeder extends Seeder { public function run() { $param = [ 'name' => 'admin', ]; DB::table('roles')->insert($param); $param = [ 'name' => 'editor', ]; DB::table('roles')->insert($param); $param = [ 'name' => 'visitor', ]; DB::table('roles')->insert($param); } }
database>seeds>RolesSeeder.php
Seedingファイルにデータを書き込み、
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { public function run() { $this->call(ArticlesSeeder::class); $this->call(AuthorsSeeder::class); $this->call(RolesSeeder::class); } }
database>seeds>DatabaseSeeder.php
DatabaseSeederへRolesSeederを登録し、
$ php artisan db:seed
database>seeds>RolesSeeder.php
Seedingを実行します。
Authorモデル編集
続いて既に作成されているAuthorモデルを編集していきます!
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Author extends Model { public function roles() { return $this->belongsToMany('App\Models\Role', 'author_role', 'author_id', 'role_id')->withTimeStamps(); } }
app>Models>Author.php
参照先外部キーが入れ替わったのみで、
Roleモデルとほぼ同じ形です。
ちなみに今回のbelongsToManyの第2第3第4引数に関しては、
Laravelの規約に沿っているのでRoleモデルAuthorモデル共に省略可能です。
中間テーブルをMigrationで作成
多対多の関係を作るためにはauthorsテーブルとrolesテーブルの他に、
中間テーブルを作成する必要があります。
名前の通り、authorsとrolesの間のテーブルです。
author_roleという中間テーブルを作成します。
$ php artisan make:migration create_author_role_table
作成されたMigrationファイルを編集します。
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAuthorRoleTable extends Migration { public function up() { Schema::create('author_role', function (Blueprint $table) { $table->increments('id'); $table->integer('author_id'); $table->integer('role_id'); $table->timestamps(); $table->unique(['author_id', 'role_id']); $table->foreign('author_id') ->references('id') ->on('authors') ->onDelete('cascade'); $table->foreign('role_id') ->references('id') ->on('roles') ->onDelete('cascade'); }); } public function down() { Schema::dropIfExists('author_role'); } }
database>migrations>xxxx_xx_xx_xxxxxx_create_author_role_table.php
author_idとrole_idを作成します。
各idは、ユニークな外部キーとして設定します。
参照先はauthod_idがauthorsテーブルのid、
role_idがrolesテーブルのidです。
onDelete('cascade')をチェーンしておくことで、
参照先テーブル(authorsもしくはroles)のデータが削除された時、
関連する中間テーブル(author_role)のデータも削除されます。
中間テーブルにデータを登録
それでは中間テーブルにデータを登録していきます。
今回はLaravelのコンソールデバッグ機能のtinkerを使用します。
$ php artisan tinker
こちらのコマンドでtinkerは実行されます。
コンソール上で対話式にデバッグを行うことができます。
$ php artisan tinker Psy Shell v0.9.9 (PHP 7.1.14 — cli) by Justin Hileman >>>
php artisan tinker とコマンドを打つことでデバッグが開始されます。
まずはauthorsテーブルのid=1一郎くんに、admin,editor,visitorと全ての権限を与えましょう。
>>> App\Models\Author::find(1)->roles()->attach([1,2,3]); => null >>>
"App\Models\Author::find(1)"にて一郎くんを取得し、
Authorモデルで先ほど作成した"->roles()"メソッドから、
更にチェーンして"->attach()"メソッドを実行します。
引数に登録したいrolesテーブルのidを指定します。
結果はnullとそっけないですが、
MySQLでauthor_roleテーブルを確認するとちゃんと登録されています。
mysql> select * from author_role; +----+-----------+---------+---------------------+---------------------+ | id | author_id | role_id | created_at | updated_at | +----+-----------+---------+---------------------+---------------------+ | 1 | 1 | 1 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | | 2 | 1 | 2 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | | 3 | 1 | 3 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | +----+-----------+---------+---------------------+---------------------+ 3 rows in set (0.00 sec)
author_id=1に、role_id=1,2,3と全ての権限が登録されています。
ちなみに確認する時はプロパティの形でアクセスします。
>>> App\Models\Author::find(1)->roles->toArray(); => [ [ "id" => 1, "name" => "admin", "created_at" => null, "updated_at" => null, "pivot" => [ "author_id" => 1, "role_id" => 1, "created_at" => "2018-11-12 07:56:39", "updated_at" => "2018-11-12 07:56:39", ], ], [ "id" => 2, "name" => "editor", "created_at" => null, "updated_at" => null, "pivot" => [ "author_id" => 1, "role_id" => 2, "created_at" => "2018-11-12 07:56:39", "updated_at" => "2018-11-12 07:56:39", ], ], [ "id" => 3, "name" => "visitor", "created_at" => null, "updated_at" => null, "pivot" => [ "author_id" => 1, "role_id" => 3, "created_at" => "2018-11-12 07:56:39", "updated_at" => "2018-11-12 07:56:39", ], ], ] >>>
同じ要領で、
authorsテーブルのid=2二郎くんにはid=2のeditorとid=3のvisitor権限を、
id=3三郎くんにはid=3のvisitor権限のみを付与します。
>>> App\Models\Author::find(2)->roles()->attach([2,3]); => null >>> App\Models\Author::find(3)->roles()->attach([3]); => null >>>
MySQLで確認します。
mysql> select * from author_role; +----+-----------+---------+---------------------+---------------------+ | id | author_id | role_id | created_at | updated_at | +----+-----------+---------+---------------------+---------------------+ | 1 | 1 | 1 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | | 2 | 1 | 2 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | | 3 | 1 | 3 | 2018-11-12 07:56:39 | 2018-11-12 07:56:39 | | 4 | 2 | 2 | 2018-11-12 08:03:16 | 2018-11-12 08:03:16 | | 5 | 2 | 3 | 2018-11-12 08:03:16 | 2018-11-12 08:03:16 | | 6 | 3 | 3 | 2018-11-12 08:03:26 | 2018-11-12 08:03:26 | +----+-----------+---------+---------------------+---------------------+ 6 rows in set (0.01 sec)
無事登録されました。
RoleモデルからAuthorを確認する。
先ほどはAuthorモデルからRoleモデルを確認しました。
もちろん逆パターンも可能です。
既に中間テーブルも登録されているので、さっそく確認します。
>>> App\Models\Role::find(2)->authors->toArray(); => [ [ "id" => 1, "name" => "一郎", "created_at" => null, "updated_at" => null, "pivot" => [ "role_id" => 2, "author_id" => 1, "created_at" => "2018-11-12 07:56:39", "updated_at" => "2018-11-12 07:56:39", ], ], [ "id" => 2, "name" => "二郎", "created_at" => null, "updated_at" => null, "pivot" => [ "role_id" => 2, "author_id" => 2, "created_at" => "2018-11-12 08:03:16", "updated_at" => "2018-11-12 08:03:16", ], ], ] >>>
role_id=2のeditor権限を取得している、
id=1一郎とid=2の二郎のデータを取得できました。
今回はここまでです、ありがとうございました〜!