初めてのLaravel 5.6 : (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.6/eloquent-relationships#many-to-many


Migration

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

php artisan make:migration create_tags_table
php artisan make:migration create_article_tag_table
<?php
// database/migrations/YYYY_MM_DD_XXXXXX_create_tags_table.php

use Illuminate\Support\Facades\Schema;
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')->unique();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('tags');
    }
}
<?php
// database/migrations/YYYY_MM_DD_XXXXXX_create_article_tag_table.php

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

class CreateArticleTagTable extends Migration
{
    public function up()
    {
        Schema::create('article_tag', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('article_id');
            $table->unsignedInteger('tag_id');
            $table->timestamps();

            $table->unique(['article_id', 'tag_id']);
            $table->foreign('article_id')
                ->references('id')
                ->on('articles')
                ->onDelete('cascade');
            $table->foreign('tag_id')
                ->references('id')
                ->on('tags')
                ->onDelete('cascade');
        });
    }

    public function down()
    {
        Schema::dropIfExists('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::pluck('name')->toArray();
=> [
       "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 アカウントを使ってコメントしています。 ログアウト /  変更 )

Google+ フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中