Laravel Eloquent“with()”渴望加载&“whereHas()”条件

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

我有2个表:'作者'和'书',具有以下简单的关系:

class Author extends Model
{
    public function books() {
        return $this->hasMany('App\Book');
    }
}

class Book extends Model 
{
    public function author() {
        return $this->belongsTo('App\Author');
    }
}

Book模型有一个属性price(float)和category(string)。

我试图在1个查询中找到一种有效的方法来做这3件事:

  1. 从日期1和日期2之间获取authors喜剧,books的所有category列表。
  2. 列表中的作者应该有一个额外的属性created_at,这是所有作者书中符合步骤1标准的最高价格。
  3. 书籍清单应按highest_price排序,作者的最终结果列表也应按该新属性price排序。

结果必须是这样的:

highest_price

到目前为止我得到了什么:

[
    {
        id: 1,
        name: 'Author ABC'
        highest_price: 15
        books: [
            {
                id: 1,
                name: 'book1',
                category: 'comedy',
                price: 15
            },
            {
                id: 2,
                name: 'book2',
                category: 'comedy',
                price: 10
            }
        ]
    },
    {
        id: 10,
        name: 'Author XYZ'
        highest_price: 12
        books: [
            {
                id: 3,
                name: 'book3',
                category: 'comedy',
                price: 12
            }
        ]
    }
]

但这只是给了我所有拥有书籍的作者,它没有考虑到急切加载的条件......

甚至可以在1个查询中吗?任何帮助,将不胜感激!

SOLUTION

感谢答案,我发现我需要的是Author::with(['books' => function($query) { $query->where('category', 'comedy') ->where('created_at', '>=', $someFromDate) ->where('created_at', '<=', $someToDate); }]) ->has('books', '>', 0) ->get() ->dd(); with()的组合(在这里找到答案:whereHas())。因为“where”条件必须在两种方法中,我在作者模型上使用了https://stackoverflow.com/a/29594039/5297218

query scope

对于额外的public function scopeWithAndWhereHas($query, $relation, $constraint){ return $query->whereHas($relation, $constraint) ->with([$relation => $constraint]); } 属性,我在highest_price上使用transform()method,结合Collection方法获得最高价格。这是最终结果:

max()

ps:这个解决方案比我以前做的高出42倍!

laravel eloquent query-builder laravel-5.7 laravel-collection
3个回答
2
投票

Author::withAndWhereHas('books', function($query) use ($category, $date){ $query->where('category', $category) ->where('created_at', '>=', $date->get('start')) ->where('created_at', '<=', $date->get('end')) ->orderBy('price', 'desc'); }) ->get() ->transform(function ($author){ $author['highest_price'] = $author->books->max('price'); return $author; }) ->sortByDesc('highest_price') ->values(); 上的子查询将限制作者与书籍的关系中加载的内容,但不会限制作者返回的内容。你尝试过with()而不是whereHas()吗?这将只返回拥有符合该标准的书籍的作者。这可以通过一个查询实现,但它将取决于您需要的所有内容。增加with()字段可能会更好地适应不同的关系。

highest_price

如果您有任何疑问,请告诉我。


0
投票

使用关系条件时有两种不同的结果:

1-根据条件限制RELATIONSHIP结果(这是你正在做的,但不是你想要的):

Author::with('books', 'highest_price')->whereHas(['books' => function($query) use($someFromDate, $someToDate) {
$query->where('category', 'comedy')
    ->where('created_at', '>=', $someFromDate)
    ->where('created_at', '<=', $someToDate);
}])
->get()->sortByDesc('highest_price.highest_price');

无论是否出版书籍,您都可以在这里找到所有作者。出版书籍的人将有书籍信息。所有其他人都不会。

2-根据关系条件限制PARENT结果(这是你应该使用的):

$authors = Author::with(['books' => function($query) {
    $query->whereNotNull('published');
}])->get();

在这里,你只有作者出版过书籍。

注意:如果您想急切加载Book模型,您仍然必须使用 - > with('books')


0
投票

从您问题的结果集中,下面应该为您提供所需内容:

$authors = Author::whereHas('books', function($query) {
     $query->whereNotNull('published');
})->get();

说明:

解决这些问题的简单方法是父查询构建器的第一个结构,在这种情况下是作者。因此,根据您的要求,我们检查了最高价格的存在,过滤了至少有一本书的作者,按最高价格desc排序,并从authors表中选择了我们需要的列。

现在,考虑到急切加载,我们做了同样的事情,过滤了created_at日期范围,按价格desc和所需的选定列进行排序。我添加了额外的列author_id,这是故意的外键,以便显示书籍结果。

© www.soinside.com 2019 - 2024. All rights reserved.