【发布时间】:2020-06-17 19:37:57
【问题描述】:
我有一个简单的问题。
我如何编写一个工厂,让我根据原始调用 make() 或 create() 定义使用 make() 或 create() 的关系?
这是我的用例:
我有一个简单的工厂
/** @var $factory Illuminate\Database\Eloquent\Factory */
$factory->define(App\User::class, function (Faker $faker) {
return [
'role_id' => factory(Role::class),
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => 'secret',
];
});
我的问题在于那个 role_id。当使用factory(Role::class) 时,它会一直创建一个角色!在数据库中写入...
在我的测试中,当我写 factory(User::class)->create(); 时,它会创建一个用户和角色,没关系!
但是如果我写factory(User::class)->make();它仍然会创建一个角色...我不会在使用make()时在数据库中写一个角色,在这种情况下,我只想要一个简单的role_id => 0。
我怎样才能做到这一点?
谢谢!
编辑(解决方法)
嗯,这比预期的要难,当您定义嵌套关系时,无法知道何时使用make() 或create()...我发现的唯一方法是使用debug_stacktrace() 方法,并寻找从工厂调用的 make 方法。
为了简化和重用,我为工厂创建了一个辅助方法associateTo($class),这个方法将定义工厂中的关系。使用create() 时,它将从相关模型返回一个现有ID,使用make() 时返回1。
通过这种方式,工厂可以:
/** @var $factory Illuminate\Database\Eloquent\Factory */
$factory->define(App\User::class, function (Faker $faker) {
return [
'role_id' => associateTo(Role::class), // Defining relationship
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => 'secret',
'remember_token' => Str::random(10),
];
});
辅助函数是:
/**
* Helper that returns an ID of a specified Model (by class name).
* If there are any Model, it will grab one randomly, if not, it will create a new one using it's Factory
* When using make() it wont write in Database, instead, it will return a 1
*
* @param string $modelClass
* @param array $attributes
* @param bool $forceNew
* @return int
*/
function associateTo(string $modelClass, array $attributes = [])
{
$isMaking = collect(debug_backtrace())
// A function called 'make()'
->filter(fn ($item) => isset($item['function']) && $item['function'] === 'make')
// file where I have global functions
->filter(fn ($item) => isset($item['file']) && Str::endsWith($item['file'], 'functions.php'))
->count();
if ($isMaking) return 1;
/** @var Model $model */
$model = resolve($modelClass);
return optional($model::inRandomOrder()->first())->id
?? create($modelClass, $attributes)->id; // call to Factory
}
有了这个,我可以轻松编写单元测试(使用工厂)而无需使用数据库连接,因此单元测试变得超快!
我希望这对其他人有帮助!
【问题讨论】:
标签: database laravel testing factory relation