初めてのLaravel 5.1 : (33) 多対多のリレーション モデル/DB編

今回は記事にタグを付与する機能を追加します。この機能の実装で、多対多のリレーションを学びます。

ブログをやっている方にはお馴染みかと思いますが、タグの仕様は以下のようになります。

  • 記事には複数のタグが付与できる(記事1件に対して、タグn件)
  • タグは複数の記事を持てる(タグ1件に対して、記事n件)
  • 記事は複数のタグに属する
  • タグは複数の記事に属する


モデル

まずはタグのモデルを作成します。

php artisan make:model Tag

以下のファイルが作成されます。

app/Tag.php

モデルに対して、多対多のリレーションを実装します。
UserモデルとArticleモデルを1対多で関連付けた時は、Articleモデルで belongsTo() メソッドを使いましたが、多対多の場合は belongsToMany() を使用します。

Article

<?php // app/Article.php
namespace App;

...

class Article extends Model
{

	...

	public function user()
	{
		return $this->belongsTo('App\User');
	}

	// 追加
	public function tags()
	{
		return $this->belongsToMany('App\Tag')->withTimestamps();
	}
}

Tag

<?php // app/Tag.php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
	protected $fillable = ['name'];

	public function articles()
	{
		return $this->belongsToMany('App\Article')->withTimestamps();;
	}
}

belongsToMany() メソッドの第1引数には関連するモデル名を渡します。

第2引数は多対多の中間テーブル名を渡します。上記の例では第2引数は省略されています。省略された場合は、モデル名をアルファベット順で並べた物が中間テーブル名となります。中間テーブル名に規約から外れた物を指定したい時に、第2引数を指定します。

return $this->belongsToMany('App\Article');
return $this->belongsToMany('App\Article', 'article_tag');
// 上記は同じです。

第3、第4キーは中間テーブルの外部キーを指定します。省略された場合は、モデル名_idが外部キーとなります。外部キーに規約から外れた物を指定したい時に、第3、第4キーを指定します。

return $this->belongsToMany('App\Article', 'article_tag');
return $this->belongsToMany('App\Article', 'article_tag', 'article_id', 'tag_id');
// 上記は同じです。

また、中間テーブルのタイムスタンプを更新する為に、withTimestamps()を使用する必要があります。

詳細は公式サイトをご覧ください。
http://laravel.com/docs/5.1/eloquent-relationships#many-to-many


Migration

tagsとarticle_tagテーブルを作成するマイグレーションを作成します。

php artisan make:migration create_tags_table --create=tags

database/migrations/YYYY_MM_DD_XXXX_create_tags_table.php を以下のように編集します。

<?php // database/migrations/YYYY_MM_DD_XXXX_create_tags_table.php

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

class CreateTagsTable extends Migration {
	public function up()
	{
		// タグテーブル
		Schema::create('tags', function(Blueprint $table)
		{
			$table->increments('id');
			$table->string('name');
			$table->timestamps();
		});

		// 記事とタグの中間テーブル
		Schema::create('article_tag', function(Blueprint $table)
		{
			$table->integer('article_id')->unsigned()->index();
			$table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');

			$table->integer('tag_id')->unsigned()->index();
			$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');

			$table->timestamps();
		});
	}

	public function down()
	{
		Schema::drop('tags');
		Schema::drop('article_tag');
	}
}

このマイグレーションではタグテーブルと共に、記事とタグの中間テーブルも作成します。中間テーブルはテーブル名を関連するモデル名をアルファベット順で並べた名前(article_tag)にし、記事テーブルとタグテーブルへの外部キーを設定します。onDelete制約を加えて、関連する記事もしくはタグが削除された時には中間テーブルのレコードも削除するように設定します。

マイグレートを実行します。

php artisan migrate

動作確認

tinker を使って、動作確認をしてみます。

タグの作成

$ php artisan tinker
>>> $tag_diary = App\Tag::create(['name' => 'diary']);
>>> $tag_work = App\Tag::create(['name' => 'work']);
>>> $tag_hobby = App\Tag::create(['name' => 'hobby']);
>>> $tag_family = App\Tag::create(['name' => 'family']);
>>> App\Tag::all()->toArray();
=> [
       [
           "id"         => "1",
           "name"       => "diary",
           "created_at" => "2015-03-28 18:11:40",
           "updated_at" => "2015-03-28 18:11:40"
       ],
       [
           "id"         => "2",
           "name"       => "work",
           "created_at" => "2015-03-28 18:11:57",
           "updated_at" => "2015-03-28 18:11:57"
       ],
       [
           "id"         => "3",
           "name"       => "hobby",
           "created_at" => "2015-03-28 18:13:18",
           "updated_at" => "2015-03-28 18:13:18"
       ],
       [
           "id"         => "4",
           "name"       => "family",
           "created_at" => "2015-03-28 18:13:45",
           "updated_at" => "2015-03-28 18:13:45"
       ]
   ]
>>> App\Tag::lists('name');
=> [
       "diary",
       "work",
       "hobby",
       "family"
   ]
>>>

記事にタグを関連付け

>>> $article = App\Article::first();
>>> $article->tags()->attach($tag_diary->id);  // ①
>>> 
>>> DB::select('select * from article_tag');
=> [
        {
           article_id: "1",
           tag_id: "1",
           created_at: "2015-03-28 21:07:56",
           updated_at: "2015-03-28 21:07:56"
       }
   ]
>>> 
>>> $article = App\Article::first();
>>> $article->tags->toArray();
=> [
       [
           "id"         => "1",
           "name"       => "diary",
           "created_at" => "2015-03-28 18:11:40",
           "updated_at" => "2015-03-28 18:11:40",
           "pivot"      => [
               "article_id" => "1",
               "tag_id"     => "1",
               "created_at" => "2015-03-28 21:07:56",
               "updated_at" => "2015-03-28 21:07:56"
           ]
       ]
   ]
>>> 

多対多のモデルを挿入するには ①attach()メソッドを使用します。

タグ側から記事を参照

上記で関連付けたタグ側から記事を参照してみます。

>>> $tag = App\Tag::first();
>>> $tag->articles->toArray();
=> [
       [
           "id"           => "1",
           "user_id"      => "3",
           "title"        => "OMNIS ARCHITECTO ODIO REPELLAT ET VOLUPTATEM BEATAE.",
           "body"         => "Minus magni est dignissimos est excepturi incidunt. Eligendi et consequatur sunt adipisci laborum corrupti repudiandae vero. Dolor eum perspiciatis enim non reiciendis.",
           "created_at"   => "2015-03-22 17:24:56",
           "updated_at"   => "2015-03-22 19:28:45",
           "published_at" => "2015-03-22 00:00:00",
           "pivot"        => [
               "tag_id"     => "1",
               "article_id" => "1",
               "created_at" => "2015-03-28 21:07:56",
               "updated_at" => "2015-03-28 21:07:56"
           ]
       ]
   ]
>>> 

多対多のリレーション作成が上手くいきました。次回は画面を通して多対多を操作を行っていきます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中