hasManyリレーションは、Eloquentのモデル間(つまり、DBのテーブル間)に1対多の関係を持たせるリレーションです。
今回は、factory()
を使って、このhasManyのリレーションを持つDBテーブルにフェイクデータを作成してみます。
hasMany
DBにおける1対多の関係では、2つのDBテーブルを親子とみなすと、親の1レコードに対してそれに紐づく子のレコードが1あるいは複数存在します。
例えば、親をusers(ユーザー)、子をaddresses(住所)とし、それぞれのEloquentのモデル、User、Addressを定義し、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", }, ], }, }メルマガ購読の申し込みはこちらから。