我有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件事:
authors
喜剧,books
的所有category
列表。created_at
,这是所有作者书中符合步骤1标准的最高价格。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个查询中吗?任何帮助,将不胜感激!
感谢答案,我发现我需要的是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倍!
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
如果您有任何疑问,请告诉我。
使用关系条件时有两种不同的结果:
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')
从您问题的结果集中,下面应该为您提供所需内容:
$authors = Author::whereHas('books', function($query) {
$query->whereNotNull('published');
})->get();
说明:
解决这些问题的简单方法是父查询构建器的第一个结构,在这种情况下是作者。因此,根据您的要求,我们检查了最高价格的存在,过滤了至少有一本书的作者,按最高价格desc排序,并从authors表中选择了我们需要的列。
现在,考虑到急切加载,我们做了同样的事情,过滤了created_at日期范围,按价格desc和所需的选定列进行排序。我添加了额外的列author_id,这是故意的外键,以便显示书籍结果。