【Laravel】MigrationとSeederとFactoryを整理する

対象 Migration、Seeder、Factoryがわからない人

書いている人 プログラミング歴2年くらい

前提 ローカルでLaravelプロジェクトを作成できる。 Eloquentを使ったことがある。

環境 PC:M1 mac Laravel: Version 9.0.1 

LaravelのMigrationとSeederとFactoryがそれぞれ頭の中でゴチャゴチャになったので整理がてらアウトプットする。
Laravelのバージョンは9です。

Laravelのプロジェクトとデータベースを用意しておくと、一緒に動きを確かめられます。

目次

Migrationとは

自分の中で、Migrationとは「テーブルの構造を決めるMigrationファイルを作成してMigrateコマンドでテーブルを作成する一連の行為自体」のことを指すと思っている。

下の画像を見たらもっと直感的にわかるかもしれない。

こういうテーブルを作成する一連の流れがMigrationだと思っている

公式では、データベースのバージョンコントロールと書かれている。

公式から。

テーブルにデータを入れる時に闇雲にでデータを入れるわけではなく、予めこのカラムにはどういう情報を入れておくかを決めておけるファイルがMigrationファイルだ。

MigrationファイルはLaravelプロジェクトを作成したらファイルが勝手に作成されている。場所は、database/migrations以下だ。

4つのmigrationファイルが既にある。

この予めあるファイルの先頭の日付は、プロジェクトを作成した日付とは全く関係がないので特に気にする必要はない。後から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');
    }
};

既にMigrationファイルを触ったことある人は気づくかもしれないが、Laravel9からreturn以下の部分がnewだけになっている。

このファイルのfunction upと書かれた部分にテーブルの各カラムにはどういうデータを入れるかが書かれている。

例えば、$table->string('name');の部分は「nameという名前で型は文字列にしてください」という意味で、ここでカラムの名前やデータの型を決められる。このファイルを実行に移すとデーターベースでテーブル構造が反映されるわけだ。

さっそくテストがてらmigrationファイルを作成してみる。

migrationファイルを作成するコマンドはphp artisan make:migration 作成するテーブル名だ。慣例としてcreate_名前_tableとしているみたいだ。

公式ではflightsテーブルを作成しているみたい。

create_ramens_tableとしてMigrationファイルを作成してみる。

php artisan make:migration create_ramens_tableと入力したら、新しくMigrationファイルが生成されるはずだ。

新しくMigrationファイルが生成された。

作成されたファイルの先頭には、作成された日時が示されている。

migrationsフォルダにも新しく追加された。

このファイルの中身が以下だ。

<?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ファイルを生成したら勝手に定義される。

function downの部分はMigrationを取り消す時に発動させる関数なのでここでは解説しません。ちなみに、初期では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したファイルとしていないファイルがわかる。

create_ramens_tableファイルはまだ実行されていない。

php artisan migrateコマンドを実行してみる。すると、まだ実行されていないMigrationファイルが実行される。

成功した!

データベースを確認してみても新しくテーブルができたことが確認できた。。

ramensテーブルが作成された。

このように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として作成してみる。

Seederクラスの作成に成功
seedersフォルダの中に新しいseederクラスが作成された。

新しく作成された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クラスだ。

公式にもデフォでは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がSeededに。

データベースの中身も確認すると、しっかりデータが追加されているのが確認できる。

jiroと反映された!

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を使うと一度に大量のデータを作成できる。たくさんのデータを生成する必要がある時に便利で主にテストに使われる。
英語では「工場」という意味なのでたくさんのデータを生成するイメージと結びつきやすいと思う。

公式では、Database Testingの欄にFactoryについて書かれている。

今回紹介するFactoryクラスは、最終的にはSeederクラスで定義していく。
具体的には、ModelとFactory、Seederクラスをそれぞれ用意してSeederクラスの中でModelを呼び出して、factoryメソッドを定義して、php artisan db:seedコマンドでデータを作成するという流れだ。

ちょっと難しいかもしれないが、順を追って見ていこう。

Laravelプロジェクト作成時にFactoryクラスはdatabase/factories/以下にUserFactory.phpというファイルが既に作成されている。

factoriesフォルダにFactoryクラスがある。
<?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プロジェクトを生成したら勝手に生成されているファイルだ。

User.phpはModelsフォルダの中に最初からある
<?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個生成されたのが確認できるはずだ。

ダミーレコードが10個生成された!

これがFactoryの基本的な使い方だが、Factoryにはもっと様々な使い方があるので、気になる人は公式リファレンスをチェックしてみてください。

公式リファレンス:https://laravel.com/docs/9.x/database-testing

新しくFactoryを作成する

これでfactoryを使えるようになったが、UserModelやUserFactoryは元々あるものなので、今度は自分で新しくFactoryを作ってみる。

Migrationの項目で生成したramensテーブルにたくさんのデータを入れることを目的とする。

今は3つしかないこのレコードにたくさんのデータを入れたい!

既にRamensSeederというSeederファイルはあるので、必要なものはRamensFactoryRamensModelの2つ。
一つずつコマンドを叩いてファイルを作成してもいいけど、LaravelにはオプションをつけることでModelとFactory一気に作る便利なコマンドがある。

php artisan make:model -f 名前コマンドでModelとFactoryを一気に生成できる。名前の部分に書いたものが、名前Model名前Factoryと反映される。

今回であればphp artisan make:model -f Ramenで、RamenModelとRamenFactoryの2つのファイルが生成される。

一気に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個データが生成されたはずだ。

レコードが10個追加された。

データを見てわかる通り、UserFactoryを使った時と比べてFakerを使っていないので、全て同じデータが入力されている。
実際にプロジェクト内でテストするならFakerを使うことをお勧めする。

まとめ

  • Migrationはテーブルの構造を決める、テーブルとカラムを作成するファイル、またはテーブルを作成することそのもの。
  • Seederとはテーブルにデータを入れるファイル、またはテーブルにデータを入れることそのもの。
  • Factoryとは大量のデータを作成するファイル、Fakerと一緒にすることが多い。Modelと関連づける必要がある。

ごちゃごちゃになっていたものが少し整理できた。Factoryに関しては、まだまだ深掘りできていないのでまた別記事にでもまとめていきたい。参考になったら幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次