У меня простой вопрос.
Как я могу написать Фабрику, которая позволяет мне определять отношения, которые используют 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, вызываемого из Factory.
Для упрощения и повторного использования я создал вспомогательный метод для Factories под названием associateTo($class)
, этот метод будет определять отношения в Factory. Он вернет существующий идентификатор из связанной модели при использовании create()
или 1 при использовании make()
.
Таким образом, фабрики могут быть:
/** @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
}
С этим я могу легко написать модульный тест (используя фабрики) без использования соединений с базой данных, поэтому модульные тесты становятся сверхбыстрыми!
Надеюсь, это поможет кому-то еще!