LaravelのMigrationとSeederとFactoryがそれぞれ頭の中でゴチャゴチャになったので整理がてらアウトプットする。
Laravelのバージョンは9です。
Laravelのプロジェクトとデータベースを用意しておくと、一緒に動きを確かめられます。
Migrationとは
自分の中で、Migrationとは「テーブルの構造を決めるMigrationファイルを作成してMigrateコマンドでテーブルを作成する一連の行為自体」のことを指すと思っている。
下の画像を見たらもっと直感的にわかるかもしれない。
公式では、データベースのバージョンコントロールと書かれている。
テーブルにデータを入れる時に闇雲にでデータを入れるわけではなく、予めこのカラムにはどういう情報を入れておくかを決めておけるファイルがMigrationファイルだ。
MigrationファイルはLaravelプロジェクトを作成したらファイルが勝手に作成されている。場所は、database/migrations以下だ。
この予めあるファイルの先頭の日付は、プロジェクトを作成した日付とは全く関係がないので特に気にする必要はない。後からmigrationファイルを作成した場合は、先頭に作成した日時が表示されるはずだ。
一番上のファイル、create_users_table.phpを開いてみる。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
};
このファイルのfunction up
と書かれた部分にテーブルの各カラムにはどういうデータを入れるかが書かれている。
例えば、$table->string('name');
の部分は「nameという名前で型は文字列にしてください」という意味で、ここでカラムの名前やデータの型を決められる。このファイルを実行に移すとデーターベースでテーブル構造が反映されるわけだ。
さっそくテストがてらmigrationファイルを作成してみる。
migrationファイルを作成するコマンドはphp artisan make:migration 作成するテーブル名
だ。慣例としてcreate_名前_table
としているみたいだ。
create_ramens_table
としてMigrationファイルを作成してみる。
php artisan make:migration create_ramens_table
と入力したら、新しくMigrationファイルが生成されるはずだ。
作成されたファイルの先頭には、作成された日時が示されている。
このファイルの中身が以下だ。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('ramens', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ramens');
}
};
function up
の部分には既にカラムの構造が定義されている。idとtimestampsだ。この2つはMigrationファイルを生成したら勝手に定義される。
public function up()
{
Schema::create('ramens', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
ここに少し構造を追加して、実際にデータベースにmigrateしてみよう。
public function up()
{
Schema::create('ramens', function (Blueprint $table) {
$table->id();
$table->timestamps();
//新しくカラムを定義
$table->string('ramen_name');
});
}
ramen_nameというカテゴリを定義してみた。
定義したら、php artisan migrate
コマンドでMigrationの実行する。
と、その前に一度php artisan migrate:status
コマンドを打ってみる。
このコマンドでphp artisan migrate:status
でmigrateしたファイルとしていないファイルがわかる。
php artisan migrate
コマンドを実行してみる。すると、まだ実行されていないMigrationファイルが実行される。
データベースを確認してみても新しくテーブルができたことが確認できた。。
このようにMigrationファイルは、テーブルとカラムの構造を作成できるファイルだ。
別にデータベースに手打ちで入力しても問題ないけど、プロジェクトが大きくなってきたり、仲間とデータベースの構造を共有したい場合などこのMigrationファイルさえあれば、データベースに接続した時にコマンドを叩くだけでテーブルの構造を作成してくれるのでめちゃくちゃ便利だ。
ちなみにMigrationの英語の意味自体は「移行」だ。
Migrationファイルがあれば、色々データベース構造を移行できると覚えよう。(若干こじつけ感があるけど)
Migrateしただけではテーブル構造を定義しただけで中身はまだ空っぽだ。データを入れたい時に便利なのが次に紹介するSeederだ。
Seederとは
Migrationでは、テーブルの構造を定義できるがそこにデータを入れることができなかった。Seederファイルを使うと、テーブルにデータを入れることができる。
Seedは英語で「種」という意味なので、データをデータベースに埋め込むという意味ではこちらは覚えやすいのではないだろうか。
database/seeders/の中にDatabaseSeeder.phpファイルがある。これはLaravelプロジェクトを作成した時に元から生成されているファイルだ。
このファイルはSeederファイルの大元みたいなもので、ここのrunメソッドには最後に紹介するFactoryに関するメソッドがコメントアウトされている。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
// \App\Models\User::factory(10)->create();
}
}
このrunメソッドの中で各Seederを呼び出して、使っていく形だ。
さっそくやっていくので、まずはSeederクラスを作成していく。
php artisan make:seeder シーダー名
で新しくシーダークラスを作成できる。今回はphp artisan make:seeder RamenSeeder
として作成してみる。
新しく作成されたRamenSeederクラスの中身は以下だ。これはDatabaseSeedrクラスと全く一緒だ。このrunメソッドの中にMigrationで作成したramenテーブルにデータを追加するコードを記述する。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class RamenSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
runメソッドの中に以下を追記。今回はModelを作成していないので、クエリビルダでデータを追加したが、もちろんModelクラスを作成してModelクラス経由でデータを追加してもOKだ。
クエリビルだを使うので、use Illuminate\Support\Facades\DB;
を忘れずに追記しておこう。
use Illuminate\Support\Facades\DB; //忘れずに追記
public function run()
{
DB::table('ramens')->insert([
'ramen_name' => 'jiro',
]);
}
後は、Seederを実行するためにphp artisan db:seed
コマンドを打つだけだがこのままだとまだデータがうまく反映されない。(オプションつければ反映されます。)
php artisan db:seed
コマンドで動くのはデフォルトでは、元から用意されているDatabaseSeederクラスだ。
なので、RamenSeederクラスを動かすには、php artisan db:seed --class=RamenSeeder
とオプションをつけて動かすかDatabaseSeederクラスのrunメソッドの中でRamenSeederを呼ぶように設定するかだ。
DatabaseSeederクラスのrunメソッドの中でRamenSeederクラスを定義して呼び出してみよう。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call([
RamenSeeder::class,
]);
}
}
runメソッドの中でcallメソッドを呼び出しその中でSeederクラスを呼び出している。callメソッドの中で配列を使えるので、仮に他のSeederクラスも作成して呼び出したい場合はここに追記していけば、php artisan db:seed
だけで一気にSeedingできるので便利だ。
public function run()
{
$this->call([
RamenSeeder::class,
]);
}
書き終えたら、php artisan db:seed
を走らせてみよう。
データベースの中身も確認すると、しっかりデータが追加されているのが確認できる。
RamenSeederファイルに、新しく追加データを記入して再度php artisan db:seed
を走らせた場合どうなるか試してみる。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Seeder;
class RamenSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('ramens')->insert([
'ramen_name' => 'jiro',
]);
DB::table('ramens')->insert([
'ramen_name' => 'taishoken',
]);
}
}
その場合、既存のレコードも含めさらにデータが追加される形になった。
差分だけ追加していくことはできなそうだ。(たぶん)
Seederでは、データを入れることができたが、たくさんのデータを一気に入れるには不向きそうだ。
たくさんのデータを入れる必要がある時に使用するのが、次に紹介するFactoryだ。
Factoryとは
最後にFactoryについて。
このFactoryは公式では、Model Factoriesとも呼ばれる。名前の通り、Modelも関係してくるのでFactoryクラスを作成する時はModelクラスも一緒に作成しよう。
Factoryを使うと一度に大量のデータを作成できる。たくさんのデータを生成する必要がある時に便利で主にテストに使われる。
英語では「工場」という意味なのでたくさんのデータを生成するイメージと結びつきやすいと思う。
今回紹介するFactoryクラスは、最終的にはSeederクラスで定義していく。
具体的には、ModelとFactory、Seederクラスをそれぞれ用意してSeederクラスの中でModelを呼び出して、factoryメソッドを定義して、php artisan db:seedコマンドでデータを作成するという流れだ。
ちょっと難しいかもしれないが、順を追って見ていこう。
Laravelプロジェクト作成時にFactoryクラスはdatabase/factories/以下にUserFactory.phpというファイルが既に作成されている。
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*
* @return static
*/
public function unverified()
{
return $this->state(function (array $attributes) {
return [
'email_verified_at' => null,
];
});
}
}
このファイルのdefinitionメソッドの中でreturnしている部分が大量にデータを作成している部分だ。
ここでUserモデルの各プロパティにデータを追加している。
fakerという見慣れない単語が出てくるが、これはダミーデータを作成してくれる便利なものだ。たくさん解説しているものがあるはずなので詳しくはググって欲しい。
次に、Userモデルを見てみよう。app/Models以下にあるUser.phpを開こう。こちらもLaravelプロジェクトを生成したら勝手に生成されているファイルだ。
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Factoryを使うには、6行目のuse Illuminate\Database\Eloquent\Factories\HasFactory;
のようにModelクラスに名前空間を書き、13行目のようにuse HasFactory;
と書く必要がある。
UserのダミーデータをUserテーブルに突っ込みたい。
ここで、Seederのとこで見たDatabaseSeeder.phpファイルでコメントアウトされていたところに注目しよう。
※上のSeederで書いたcallメソッドは消してます。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
\App\Models\User::factory(10)->create();
}
}
このrunメソッドの中に定義されている部分が、実際にfactoryを使ってデータを埋め込んでいるところだ。
public function run()
{
\App\Models\User::factory(10)->create();
}
見ればわかるように、Userモデルからfactoryメソッドを呼び出している。このfactoryメソッドが実際factoryを使ってデータを作成している部分だ。
factoryメソッドはモデル名Factory
クラスをFactoryの中から探してくる。つまり、UserFactoryクラスを探してくるというわけだ。
引数に10と渡されているのはデータを10個作成してくださいという意味で、最後のcreate()
の部分でデータを保存している。
実際にデータベースにデータ(レコード)を追加したいので、php aritsan db:seed
コマンドを入力してみよう。
するとusersテーブルの中にダミーレコードが10個生成されたのが確認できるはずだ。
これがFactoryの基本的な使い方だが、Factoryにはもっと様々な使い方があるので、気になる人は公式リファレンスをチェックしてみてください。
公式リファレンス:https://laravel.com/docs/9.x/database-testing
新しくFactoryを作成する
これでfactoryを使えるようになったが、UserModelやUserFactoryは元々あるものなので、今度は自分で新しくFactoryを作ってみる。
Migrationの項目で生成したramensテーブル
にたくさんのデータを入れることを目的とする。
既にRamensSeederというSeederファイルはあるので、必要なものはRamensFactoryとRamensModelの2つ。
一つずつコマンドを叩いてファイルを作成してもいいけど、LaravelにはオプションをつけることでModelとFactory一気に作る便利なコマンドがある。
php artisan make:model -f 名前
コマンドでModelとFactoryを一気に生成できる。名前の部分に書いたものが、名前Model
、名前Factory
と反映される。
今回であればphp artisan make:model -f Ramen
で、RamenModelとRamenFactoryの2つのファイルが生成される。
まずは、ModelクラスであるRamen.phpファイルから。
このファイルにはuse Illuminate\Database\Eloquent\Factories\HasFactory;
とuse HasFactory;
が記載されている。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Ramen extends Model
{
use HasFactory;
}
次は、RamenFactory.php。
ファイルにはdefinitionメソッドが定義されている。
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Ramen>
*/
class RamenFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
//
];
}
}
また、7~9行目の部分のコメント部分に、RamenModelと親切にも書かれている。
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Ramen>
*/
さっそく、RamenFactory.phpを以下のように変更する。
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Ramen>
*/
class RamenFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'ramen_name' => "ramen1",
];
}
}
定義した部分は、return[ ]の中身だけ。keyと値をセットで記入する。もちろん、keyにはそのテーブルにあるカラム(=モデルにあるプロパティ)でないと受付けないので気をつけよう。
次に、RamenSeeder.phpに変更を加える。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Seeder;
class RamenSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
\App\Models\Ramen::factory(10)->create();
}
}
runメソッドの中でRamenモデルからfactoryメソッドを呼び出して、createで保存する。
RamenModelファイルには特になにも記入していないが、RamenModelを通してfactoryメソッドを呼ぶ必要があるのでModelファイルの作成が必要になってくる。
これで準備ができたので、後は、seedingしていく。php artisan db:seed
にオプションをつけた場合、特定のSeederだけ動せるので以下のコマンドを実行しよう。php artisan db:seed --class RamenSeeder
実行すると10個データが生成されたはずだ。
データを見てわかる通り、UserFactoryを使った時と比べてFakerを使っていないので、全て同じデータが入力されている。
実際にプロジェクト内でテストするならFakerを使うことをお勧めする。
まとめ
- Migrationはテーブルの構造を決める、テーブルとカラムを作成するファイル、またはテーブルを作成することそのもの。
- Seederとはテーブルにデータを入れるファイル、またはテーブルにデータを入れることそのもの。
- Factoryとは大量のデータを作成するファイル、Fakerと一緒にすることが多い。Modelと関連づける必要がある。
ごちゃごちゃになっていたものが少し整理できた。Factoryに関しては、まだまだ深掘りできていないのでまた別記事にでもまとめていきたい。参考になったら幸いです。