【问题标题】:Laravel eager loading phpunit testingLaravel 急切加载 phpunit 测试
【发布时间】:2020-03-05 05:28:29
【问题描述】:

我正在尝试执行 GET 请求以通过 ID /GET 'film/{id}' 等检索特定电影

film_table

--标识

--描述

这是响应的一部分,如果我在电影模型中有多对多关系,如film 模型所示

public function languages(): BelongsToMany
{
    return $this->belongsToMany(
        'App\Models\Language',
        'film_languages',
        'film_id',
        'language_code'
    );
}

public function categories(): BelongsToMany
{
    return $this->belongsToMany(
        'App\Models\Category',
        'film_categories',
        'film_id',
        'category_id'
    );
}

有没有一种方法可以将这些表作为响应的一部分包含在内我在 Laravel 文档中遇到了一种称为急切加载的内容,但不确定这是否涵盖了我需要的内容我在下面有一个示例,但如果我是在正确的轨道上?

FilmController

public function show(string $id): string
{
    /** @var Film  $film*/
    $film = film::findOrFail($id)
        ->with(
            'languages',
            'categories',
            )->get();

    return $film->toJson();

我正在尝试编写一个 PHPUnit 测试,以确保当通过 id 抓取影片时,它返回 iddescription 以及从多对多关系中选择的选项。

这是我已经开始的测试

FilmControllerTest

public function setUp(): void
{
    parent::setUp();

    $this->film = factory(Film::class)->create();

    $languages = factory(Language::class)->create();

    $categories = factory(Category::class)->create();
}

/**
 * @test
 */
public function it_should_get_film()
{
    $response = $this->json('GET', '/film/' . $this->film->id);

    $film= Film::findOrFail($this->film->id);

    $response->assertJson([
        'id' => $film->id,
        'description' => 'This is my favourite film'
    ]);
}

您能否将这些关系包含在工厂中?

FilmFactory

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

LanguagesFactory

  $factory->define(\App\Models\Language::class, function (Faker $faker){
    $langCode = $faker->languageCode;
    return [
        'code' => $langCode,
        'name' => $langCode,
    ];
});

CategoriesFactory

    $factory->define(\App\Models\Category::class, function (Faker $faker){

    return [
        'main' => $faker->text,
        'sub' => $faker->text,
    ];
});

我能在这方面得到一些帮助吗?

【问题讨论】:

    标签: php laravel phpunit eager-loading


    【解决方案1】:

    你走在正确的轨道上。但是,存在一些语法错误。

    这是您抓取影片、急切加载所需关系并将其返回为 JSON 的方式:

    public function show($id)
    {
        $film = Film::with(['languages', 'categories'])->findOrFail($id);
    
        return $film; // will automatically convert it to JSON
    }
    

    这就是你测试它的方式:

    public function testShowFilm()
    {
        $film = factory(Film::class)->create();
    
        $language = factory(Language::class)->create();
    
        $film->languages()->attach($language->id); // associate the film with the language
    
        $category = factory(Category::class)->create();
    
        $film->categories()->attach($category->id); // associate the film with the category
    
        $url = '/films/' . $film->id;
    
        $this->json('GET', $url)
            ->assertStatus(200)
            ->assertJsonPath(['id' => $film->id])
            ->assertJsonPath(['description' => $film->description])
            ->assertJsonPath(['languages.0.id' => $language->id])
            ->assertJsonPath(['categories.0.id' => $category->id]);
    }
    

    请注意,在您的场景中,您应该:

    • 3 个模型表:电影、类别、语言;
    • 每个模型表的工厂;
    • 2 个关系表:film_categories 和 film_languages。

    然后您可以使用上面示例中的attach 方法将关系设置为测试的一部分,或者您可以使用工厂回调:

    https://laravel.com/docs/6.x/database-testing#factory-callbacks

    【讨论】:

    • 哇,谢谢老兄,这帮了很多忙,出于好奇,0 代表什么? ['languages.0.id' => $language->id])\ 谢谢 :D
    • @Bwinnyz 表示集合中的第一项。假设您在电影和类别/语言之间存在一对多关系,::with(['categories', 'languages']) 将返回相应对象的集合。
    • 好吧,如果它是多对多关系呢?你只有categories.id
    • @Bwinnyz,对不起,当然是多对多。但同样适用。当你拉出一部电影时,它不会有一个单一的类别,而是一个类别的集合(即使该集合可能只包含一个项目)。所以你不能使用$film->category访问第一个类别,你需要使用$film->categories[0]访问它,在上述测试的上下文中转换为categories.0
    • 好的,有道理,谢谢你帮我解决这个问题:)
    猜你喜欢
    • 2014-08-13
    • 2021-04-23
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2013-05-28
    • 1970-01-01
    相关资源
    最近更新 更多