Laravel - 添加关系到工厂创建的模型

问题描述 投票:0回答:2

我正在测试一个包含多对多关系的急切加载关系。现在我有测试中的查询和附件。我想知道是否有办法将它们移入工厂,而不是将其作为测试的一部分。这将限制测试的规模,然后每次创建薄膜工厂时都可以创建和使用这些关系。

test

public function grabFilmTest()
{
    $film = factory(Film::class)->create();

    $categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
    $languages = Languages::where('name', 'english')->first();

    $film->categories()->attach($categories->id);
    $film->languages()->attach($languages->id);

    $response = $this->json('GET', '/film/' . $film->id)
        ->assertStatus(200);

    $response
        ->assertExactJson([
            'id' => $film->id,
            'name' => $film->name,
            'description' => $film->description,
            'categories' => $film->categories->toArray(),
            'languages' => $film->languages->toArray()

}

filmFactory

$factory->define(\App\Models\Film::class, function (Faker $faker){
    return [
        'id' => $faker->uuid,
        'name' => $faker->text,
        'description' => $faker->paragraph,
    ];
});

如果有人可以帮助我如何做到这一点或一个例子,那就太好了:D

php laravel eloquent phpunit
2个回答
2
投票

您可以使用工厂状态工厂回调

$factory->define(\App\Models\Film::class, function (Faker $faker){
    return [
        'id' => $faker->uuid,
        'name' => $faker->text,
        'description' => $faker->paragraph,
    ];
});
$factory->define(\App\Models\Category::class, function (Faker $faker){
    return [
        // Category fields 
    ];
});
$factory->define(\App\Models\Language::class, function (Faker $faker){
    return [
        // Language fields 
    ];
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-category', function (\App\Models\Film $film) {
    $category = factory(\App\Models\Category::class)->create();
    $film->categories()->attach($category->id);
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-language', function (\App\Models\Film $film) {
    $language = factory(\App\Models\Language::class)->create();
    $film->categories()->attach($language->id);
});

然后你可以在这样的测试中使用:

public function grabFilmTest()
{
    $film = factory(Film::class)->create();
    $filmWithCategory = factory(Film::class)->state('with-category')->create();
    $filmWithLanguage = factory(Film::class)->state('with-language')->create();
    $filmWithCategoryAnLanguage = factory(Film::class)->states(['with-category', 'with-language'])->create();
    // ...
}

PS:我不建议使用现有数据。根据经验,我可以告诉你这会变得非常痛苦。


1
投票

为后代保留旧答案,但这就是现代版本 Laravel 中等效工厂类的样子:

<?php

namespace Database\Factories;

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

class FilmFactory extends Factory
{
    protected $model = Film::class;

    public function definition(): array
    {
        return [
            'id'          => $this->faker->uuid,
            'name'        => $this->faker->text,
            'description' => $this->faker->paragraph,
        ];
    }

    public function configure(): static
    {
        return $this->afterCreating(function (Film $film) {
            $category = Category::where('main-cat', 'Science')
                ->where('sub-cat', 'Fiction')
                ->first();
            $film->categories()->attach($category);

            $language = Languages::where('name', 'english')
                ->first();
            $film->languages()->attach($language);
        });
    }
}

您可以使用工厂回调在工厂文件中执行此操作:

<?php
use \App\Models\Film;
use \App\Models\Category;
use \App\Models\Languages;

$factory->define(Film::class, function(Faker $faker){
    return [
        'id'          => $faker->uuid,
        'name'        => $faker->text,
        'description' => $faker->paragraph,
    ];
});

$factory->afterCreating(Film::class, function(Film $film, Faker $faker) {
    $category = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
    $language = Languages::where('name', 'english')->first();
    $film->categories()->attach($category);
    $film->languages()->attach($language);
});
© www.soinside.com 2019 - 2024. All rights reserved.