初めてのLaravel 5.6 : (31) Relationships

今回は、モデルで1対nのリレーションを扱いたいと思います。ユーザーが複数の記事を持つ様に、User モデルと Article モデルを関連付けます。


Model

UserモデルとArticleモデルを1対nで関連付けます。

User

// app/User.php

class User extends Authenticatable
{
    ...
    public function articles() 
    {
        return $this->hasMany('App\Article');
    }
}

articles() メソッドを作成し、hasMany() メソッドで Article と関連付けます。
これで以下のように User に関連付く Article をn件取得できるようになります。

$articles = User::find(1)->articles();

Article

// app/Article.php

class Article extends Model
{
    ...
    public function user() 
    {
        return $this->belongsTo('App\User');
    }
}

user() メソッドを作成し、belongsTo() メソッドで User と関連付けます。
これで以下の様に Article に関連付く User を1件取得できるようになります。

$user = Article::find(1)->user();

Migration

Articles テーブルに Users テーブルへの外部キーを追加します。以前に作成したマイグレーションファイルを直接修正し、DB は全てロールバックして、初めから再構築したいと思います。

<?php
// database/migrations/YYYY_MM_DD_TTTTTT_create_articles_table.php

...

class CreateArticlesTable extends Migration
{
    public function up()
    {
        Schema::create('articles', function(Blueprint $table)
        {
            $table->increments('id');
            $table->unsignedInteger('user_id');    // 追加
            $table->string('title');
            $table->text('body');
            $table->timestamps();

            // 外部キーを追加
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade');
        });
    }

    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

user_id を外部キーとして追加しました。外部キー制約に onDelete(‘cascade’) を指定し、親レコードである Users のデータが削除されたら、子レコードである Articles のデータも削除するように指定しました。

では、以下のように artisan コマンドでDBを全てロールバックし、再度全マイグレーションを実行します。データは全て消えてしまうのでご注意ください。

php artisan migrate:refresh

もし、ここで以下のエラーが出るようでしたら、composer で doctrine/dbal を追加してください。追加後にマイグレーションができるようになります。DB に sqlite を使っている場合にこうなるようです。

Symfony\Component\Debug\Exception\FatalThrowableError : Class ‘Doctrine\DBAL\Driver\PDOSqlite\Driver’ not found

composer require doctrine/dbal

php artisan migrate:refresh

Seed

Articles テーブルの変更に伴い、Seedも修正します。

UsersTableSeeder の追加

UsersTableSeeder.php を追加します。

<?php
// database/seeds/UsersTableSeeder.php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('users')->delete();

        App\User::create([
            'name' => 'root',
            'email' => 'root@example.com',
            'password' => Hash::make('password'),
        ]);
    }
}

DatabaseSeeder の修正

UsersTableSeeder の呼び出しを追加します。

<?php
// database/seeds/DatabaseSeeder.php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call([
            UsersTableSeeder::class,  // 追加
            ArticlesTableSeeder::class,
        ]);
    }
}

ArticleFactory の修正

user_id 項目を追加します。

<?php
// database/factories/ArticleFactory.php

use Faker\Generator as Faker;
use Carbon\Carbon;

$factory->define(App\Article::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence(),
        'body' => $faker->paragraph(),
        'published_at' => Carbon::today(),
        // 追加
        'user_id' => function () {
            return factory(App\User::class)->create()->id;
        },
    ];
});

現状では、Article を1件作成する毎に User が1件作成されます。

ArticlesTableSeeder の修正

<?php
// database/seeds/ArticlesTableSeeder.php

use Illuminate\Database\Seeder;

class ArticlesTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('articles')->delete();

        $user = App\User::first(); // 追加

        factory(App\Article::class, 20)->create([ // 修正
            'user_id' => $user->id,
        ]);
    }
}

ユーザーを1件取得してから、Article をそのユーザーに関連付けて作成しています。

factory の create() メソッドに項目名と値の連想配列を渡すことで、factory で定義してる項目の設定内容を上書きすることができます。ArticleFactory の定義内容では1件の Article に対して毎回ユーザーを作成して user_id をセットするようになっていますが、それを上書きして、検索したユーザーの ID をセットしています。

Seeding

artisan コマンドで seed を実行します。

php artisan db:seed

これでテスト用の Article データをユーザーと関連付けたデータとして用意することができました。


Controller

新規の記事を、ログイン中のユーザーの記事として保存するよう修正します。

// app/Http/Controllers/ArticlesController.php

...

use Illuminate\Support\Facades\Auth;

class ArticlesController extends Controller {
    ...
    public function store(ArticleRequest $request) {
        // Article::create($request->validated());
        Auth::user()->articles()->create($request->validated());

        return redirect()->route('articles.index')
            ->with('message', '記事を追加しました。');
    }
    ...
}

動作確認

ログインして、記事を新規作成した後に、tinkerでデータを確認してみます。
新規のユーザーを http://localhost:8000/auth/register から登録して、色々試してみてください。

$ php artisan tinker
>>>
>>> $user = App\User::where("name", "who")->first();
>>>
>>> $user->articles->count();
=> 2
>>>
>>> $user->articles->toArray();
=> [
       [
           "id"           => "11",
           "user_id"      => "5",
           "title"        => "WHOの記事",
           "body"         => "かくかくrnしかじか",
           "created_at"   => "2015-03-24 11:56:17",
           "updated_at"   => "2015-03-24 11:56:17",
           "published_at" => "2015-03-24 00:00:00"
       ],
       [
           "id"           => "12",
           "user_id"      => "5",
           "title"        => "WHOの2件目の記事",
           "body"         => "foornbar",
           "created_at"   => "2015-03-24 12:05:31",
           "updated_at"   => "2015-03-24 12:05:31",
           "published_at" => "2015-03-24 00:00:00"
       ]
   ]
>>>
>>>
>>> $article = App\Article::find(11);
>>>
>>> $article->user->toArray();
=> [
       "id"         => "5",
       "name"       => "who",
       "email"      => "who@sample.com",
       "created_at" => "2015-03-24 11:55:55",
       "updated_at" => "2015-03-24 11:55:55"
   ]
>>> 
  • $user->articlesでユーザーモデルから記事にアクセス出来ました。
  • $article->userで記事モデルからユーザーモデルにアクセス出来ました。

まとめ

hasMany(), belongsTo() を使って、モデルの1対nのリレーションを作ることが出来るようになりました。

Eloquentのリレーションには、1対nの他にも、1対1やn対n等もありますので、公式サイトには目を通しておいてください。

http://laravel.com/docs/5.6/eloquent-relationships

広告

コメントを残す

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

WordPress.com ロゴ

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

Google+ フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中