hasManyリレーションは、Eloquentのモデル間(つまり、DBのテーブル間)に1対多の関係を持たせるリレーションです。
今回は、factory()を使って、このhasManyのリレーションを持つDBテーブルにフェイクデータを作成してみます。

hasMany

DBにおける1対多の関係では、2つのDBテーブルを親子とみなすと、親の1レコードに対してそれに紐づく子のレコードが1あるいは複数存在します。

例えば、親をusers(ユーザー)、子をaddresses(住所)とし、それぞれのEloquentのモデル、UserAddressを定義し、User::addresses()のリレーションを定義すれば、以下のようなデータとなります。

>>> User::find(1)->load('addresses');
=> App\User {#2312
     id: 1,
     name: "原田 涼平",
     email: "esasada@example.com",
     created_at: "2019-01-12 01:53:58",
     updated_at: "2019-01-12 01:53:58",
     addresses: Illuminate\Database\Eloquent\Collection {#2340
       all: [
         App\Address {#2349
           id: 1,
           user_id: 1,
           address: "8731301  山口県石田市中央区田中町高橋1-5-6 コーポ高橋110号",
           created_at: "2019-01-12 01:53:58",
           updated_at: "2019-01-12 01:53:58",
         },
         App\Address {#2348
           id: 2,
           user_id: 1,
           address: "7159410  京都府坂本市北区加納町加藤9-7-8",
           created_at: "2019-01-12 01:53:58",
           updated_at: "2019-01-12 01:53:58",
         },
       ],
     },
   }

ここでは、原田さんは2つの住所を持つことが表示されています。もちろん、これらはフェイクデータですが、次の準備をすればこれがtinkerの1行の実行で親子のレコードを作成できます。

準備

Laravelの新規のプロジェクトを作成すると、usersのmigration、model、factoryがすでに定義されています。ここで必要なのは、addressesにおいて同様な定義です。

まず、migrationから

$ php artisan make:migration create_addresses_table --create=addresses

上のコマンドラインでの実行で、

$ ls -1 database/migrations 
2014_10_12_000000_create_users_table.php
2014_10_12_100000_create_password_resets_table.php
2019_01_10_140631_create_addresses_table.php

2019_01_10_140631_create_addresses_table.phpのファイルが作成されます。これを以下のように編集。

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

class CreateAddressesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('addresses', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->string('address');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('addresses');
    }
}

users.idと紐づけるために、addresses.user_idの項目が追加されていることに注意してください。

そして、次を実行して、addressesのDBテーブルを作成します。

$ php artisan migrate
Migrating: 2019_01_10_140631_create_addresses_table
Migrated:  2019_01_10_140631_create_addresses_table

次に、モデルを作成。これもコマンドラインで実行。

$ php artian make:model Address

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

app/Address.php

そしてこれを以下のように編集。

namespace App;

use Illuminate\Database\Eloquent\Model;

class Address extends Model
{
    protected $fillable = [
        'address'
    ];
}

Addressのモデルができたところで、UserのモデルにhasManyのリレーション、addresses()を定義します。

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Auth\Passwords\CanResetPassword;

class User extends Authenticatable
{
    use Notifiable;
	use CanResetPassword;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    public function addresses()
    {
        return $this->hasMany('App\Address');
    }
}

そして、Addressのフェイクデータを作成するためにfactoryを作成します。

$ php artisan make:factory AddressFactory

この実行でAddressFactory.phpのファイルが作成されます。

$ ls -1 database/factories 
AddressFactory.php
UserFactory.php

これを以下のように編集。

use Faker\Generator as Faker;                                                                                                                                                                                      
                                                                                                                                                                                                                   
$factory->define(App\Address::class, function (Faker $faker) {                                                                                                                                                     
    return [                                                                                                                                                                                                       
       'address' => $faker->address                                                                                                                                                                                
    ];                                                                                                                                                                                                             
});                                                                                                                                                                                                                

最後に、フェイクデータを日本語で作成するために、config/app.phpに以下を追加します。

..
    'faker_locale' => 'ja_JP', 
..

これで準備終わり。

フェイクデータの作成

さて、tinkerの出番です。

$ php artisan tinker
>>> factory(App\User::class)->create()->each(function ($user) { $user->addresses()->save(factory(App\Address::class)->make()); });
=> true
>>> User::find(1)->load('addresses');
=> App\User {#2310
     id: 1,
     name: "近藤 亮介",
     email: "mikako.koizumi@example.net",
     created_at: "2019-01-12 02:56:16",
     updated_at: "2019-01-12 02:56:16",
     addresses: Illuminate\Database\Eloquent\Collection {#2347
       all: [
         App\Address {#2349
           id: 1,
           user_id: 1,
           address: "8657778  滋賀県松本市南区中島町笹田9-6-2",
           created_at: "2019-01-12 02:56:16",
           updated_at: "2019-01-12 02:56:16",
         },
       ],
     },
   }

factoryの実行文が長いので、整形して表示してみましょう。

factory(App\User::class)->create()
    ->each(function ($user) {
        $user->addresses()
            ->save(
               factory(App\Address::class)->make()
         ); 
    });

1行目で、usersのレコードが作成され、それぞれのusersのレコードにおいて、addresses()のリレーションをもとにaddressesのレコードが作成されます。

1つのusersのレコードに対して、addressesのレコードが複数欲しいなら、save()の部分を

saveMany(factory(App\Address::class, 2)

に置き換えます。以下のように2つaddressesのレコード作成となります。

>>> factory(App\User::class)->create()->each(function ($user) { $user->addresses()->saveMany(factory(App\Address::class, 2)->make()); });
=> true
>>> User::find(2)->load('addresses');
=> App\User {#2368
     id: 2,
     name: "津田 七夏",
     email: "kudo.kaori@example.org",
     created_at: "2019-01-12 02:59:24",
     updated_at: "2019-01-12 02:59:24",
     addresses: Illuminate\Database\Eloquent\Collection {#2369
       all: [
         App\Address {#2354
           id: 4,
           user_id: 2,
           address: "8621292  長崎県石田市中央区大垣町渡辺3-6-8",
           created_at: "2019-01-12 02:59:24",
           updated_at: "2019-01-12 02:59:24",
         },
         App\Address {#2357
           id: 5,
           user_id: 2,
           address: "6857809  沖縄県三宅市南区山本町山岸9-5-9 コーポ山口101号",
           created_at: "2019-01-12 02:59:24",
           updated_at: "2019-01-12 02:59:24",
         },
       ],
     },
   }

By khino