🏭

Laravelのモデルファクトリ 新旧が共存することが可能か検証してみた

2022/03/27に公開

Laravel8でモデルファクトリがクラスベースに書き直され、それを楽に修正するために以前こんな記事を書きました。
https://zenn.dev/naopusyu/articles/cc68a0aa827bca

ただ、ツールを使って一気に修正することが困難なときもあると思うので、あとから修正する場合に新旧の書き方が共存することが可能なのか検証してみました。

環境

  • PHP 8.1.2
  • Laravel 9.5.1

環境はLaravel Sailで作っています。

事前準備

Laravel7までの旧モデルファクトリを使うためにlaravel/legacy-factoriesパッケージを入れておきます。
https://github.com/laravel/legacy-factories

./vendor/bin/sail composer req laravel/legacy-factories --dev

さらに検証用のモデルクラスを用意します。
今回はLaravelのドキュメントに書いてある内容を使います。
https://laravel.com/docs/9.x/eloquent#generating-model-classes

app/Models/Flight.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    use HasFactory;
}

検証1 異なるEloquentモデルクラスが新旧の書き方をしている

新の方は、元々からあるUserクラスを使います。
旧の方は、こちらを使います。

database/factories/FlightOldFactory.php
<?php

use App\Models\Flight;
use Faker\Generator as Faker;

$factory->define(Flight::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
    ];
});

検証はtinkerを使って新旧それぞれのモデルファクトリを実行してみます。

$ ./vendor/bin/sail artisan tinker
Psy Shell v0.11.2 (PHP 8.1.2 — cli) by Justin Hileman
>>> 
>>> use App\Models\User;
>>> $user = User::factory()->make();
=> App\Models\User {#3533
     name: "Lew Langosh DDS",
     email: "demarcus13@example.net",
     email_verified_at: "2022-03-26 13:40:50",
     #password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
     #remember_token: "Pz9m6zZmuI",
   }
>>> 
>>> use App\Models\Flight;
>>> $flight = factory(Flight::class)->make();
=> App\Models\Flight {#3527
     name: "Dr. Zetta Kilback",
   }
>>> 

検証2 同じEloquentモデルクラスが新旧の書き方をしている パターン1

新の方は、こちらを使います。

database/factories/FlightFactory.php
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Flight>
 */
class FlightFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name,
        ];
    }
}

旧の方は、さきほどの使った内容を使います。
ただし、新の方は次のような条件[1]があるため、旧の方のファイル名を別名に変更しています。

  • ネームスペースがDatabase\Factoriesの中にある
  • クラス名がモデル名 + Factoryである
database/factories/FlightFactory.php
↓
database/factories/FlightOldFactory.php

検証はさきほどと同じでtinkerを使って新旧それぞれのモデルファクトリを実行してみます。

$ ./vendor/bin/sail artisan tinker
Psy Shell v0.11.2 (PHP 8.1.2 — cli) by Justin Hileman
>>> 
>>> 
>>> use App\Models\Flight;
>>> $flight = Flight::factory()->make();
=> App\Models\Flight {#3532
     name: "Lora Gorczany",
   }
>>> $flight = factory(Flight::class)->make();
=> App\Models\Flight {#3496
     name: "King Cole DVM",
   }
>>> 

検証3 同じEloquentモデルクラスが新旧の書き方をしている パターン2

ドキュメントを読むとHasFactoryトレイトのnewFactoryメソッドをオーバーライドして、任意のファクトリクラスのインスタンスを返すように変更すれば、検証2の新のファクトリクラスの命名ルールを適用しないで済みます。
この場合、旧ファクトリのファイル名を変更する必要はないです。

app/Models/Flight.php
use Database\Factories\NewFactory\FlightFactory;

~~~~~~~~省略~~~~~~~~

    protected static function newFactory()
    {
        return new FlightFactory();
    }

あとファクトリクラスの$modelプロパティにEloquentモデルクラスを指定してあげる必要があります。

database/factories/NewFactory/FlightFactory.php
<?php

namespace Database\Factories\NewFactory;

use App\Models\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Flight>
 */
class FlightFactory extends Factory
{
    protected $model = Flight::class; // これを追加

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'name' => 'aaa',
        ];
    }
}
$ ./vendor/bin/sail artisan tinker
Psy Shell v0.11.2 (PHP 8.1.2 — cli) by Justin Hileman
>>> 
>>> 
>>> use App\Models\Flight;
>>> $flight = Flight::factory()->make();
=> App\Models\Flight {#3529
     name: "aaa",
   }
>>> $flight = factory(Flight::class)->make();
=> App\Models\Flight {#3528
     name: "Dr. Ezra Stroman",
   }
>>> 

結果

3つの検証を試してみましたが、laravel/legacy-factoriesパッケージを使うことで新旧の書き方が共存することは可能のようです。

なので、あとから地道に修正することもできそうですが、laravel/legacy-factoriesパッケージがどのバージョンのLaravelまでサポートしてくれるのかわからないので、早めに修正は行った方が良さそうです。
※Laravel9はサポートしてくれています[2]

脚注
  1. https://laravel.com/docs/9.x/database-testing#factory-and-model-discovery-conventions ↩︎

  2. https://github.com/laravel/legacy-factories/pull/13 ↩︎

Discussion