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",
},
],
},
}
メルマガ購読の申し込みはこちらから。
