Laravel8からEloquentを利用したModelFactoryが刷新され、使いやすくなりました。

Class化したおかげでメソッドチェーンで予め定義したデータの状態を簡単にCreateすることが出来るようになりました。

便利になったのでしっかりと使いこなしていきましょう。

環境

  • PHP v7.4
  • Laravel v8.31

詳細

まずはEloquentModelを作成しましょう。Eloquent\Modelを継承してWorkModelを作成しましょう。リファレンス通りにHasFactoryトレイトを使ってFactory機能を実装することでFactoryを使う準備をします。

<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Work extends Model
{
    use HasFactory;

    protected $table = 'works';

    protected $primaryKey = 'work_id';

    public $incrementing = false;

    public $timestamps = false;

}

基本的なEloquentModelを作成したら次はFactoryを作成しましょう。

Factoryモデルの作成

database/factoriesフォルダ配下が基本的な配置場所となります。最初から存在するUserFactoryを元に作成するのも良いでしょう。$modelプロパティでEloquentModelクラスを指定します。

definitionメソッドでWork::factory()->create()などで呼び出した時のデフォルト値となります。ここではFakerを使ってランダムにしていますが、固定値が必要であれば固定値を指定しましょう。

<?php

namespace Database\Factories;

use App\Models\Work;
use App\Domain\Model\Status;
use Illuminate\Database\Eloquent\Factories\Factory;

class WorkFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Work::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'work_id'                   => $this->faker->unique()->randomNumber(),
            'company_id'                => $this->faker->unique()->randomNumber(),
            'status'                    => Status::CLOSE,
        ];
    }

}

基本形はこのような形で記載します。

状態を定義する

基本形を作成しましたが、このままでは応用が利かないので作成したいデータには状態を定義してあげましょう。

有効や無効、有料無料などを定義してあげることで作成したいデータをメソッドチェーンを使って作成することができます。

<?php


namespace Database\Factories;

use App\Models\Work;
use App\Domain\Model\Status;
use Illuminate\Database\Eloquent\Factories\Factory;

class WorkFactory extends Factory
{
    /** * The name of the factory's corresponding model. * * @var string */
    protected $model = Work::class;

    /** * Define the model's default state. * * @return array */
    public function definition()
    {
        return [
            'work_id'    => $this->faker->unique()->randomNumber(),
            'company_id' => $this->faker->unique()->randomNumber(),
            'status'     => Status::CLOSE,
        ];
    }

    public function companyId($companyId): Factory
    {
        return $this->state(function () use ($companyId) {
            return ['company_id' => $companyId,];
        });
    }

    public function free(): Factory
    {
        return $this->state(function () {
            return ['status' => Status::FREE,];
        });
    }

    public function paid(): Factory
    {
        return $this->state(function () {
            return ['status' => Status::PAID,];
        });
    }
}

呼び出し方

作成したEloquentModelからfactory()メソッドから生成します。UnitTestやSeederなどで使えるでしょう

public function testWork()
{
    $companyId = 12345;
    // デフォルト値で生成する。ループを回して値を取得したりsave()することも出来る
    $works     = Work::factory()->make();
    // 作成と同時にDBに挿入
    $works     = Work::factory()->count(3)->companyId($companyId)->paid()->create();
}

あこれでLaravel8標準的なFactoryを使用することが出来ました。

お疲れ様でした。

おまけ

標準のFactoryModelの配置場所はEloquentModelのnamespaceに応じて決定されます。DDDなどのドメインパターンを採用している場合、ディレクトリ構成が冗長になってしまったりFactoryModelの置き場所を指定したい場合があるでしょう。

そのときはHasFactoryトレイトのnewFactoryメソッドをオーバーライドします。

Eloquentoモデル内に以下のように記載してあげましょう。

protected static function newFactory():Factory
{
    return \Database\Factories\Effect\WorkFactory::new();
}
おすすめの記事