我有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
(浮动)和category
(字符串)。
我正在尝试找到一种有效的方法来在1个查询中完成这3件事:
authors
喜剧,books
中获取category
中具有created_at
的所有highest_price
的列表。 price
,这是与步骤1中的条件相匹配的所有作者书籍中的最高价格。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
}
]
}
]
进行排序。结果必须是这样的:
Author::with(['books' => function($query) {
$query->where('category', 'comedy')
->where('created_at', '>=', $someFromDate)
->where('created_at', '<=', $someToDate);
}])
->has('books', '>', 0)
->get()
->dd();
到目前为止,我得到的是:
with()
但这只是给我所有拥有书籍的作者,它没有考虑到急切加载中的情况...
在1个查询中是否有可能? 任何帮助将不胜感激!
多亏了答案,我发现我需要的是whereHas()
和public function scopeWithAndWhereHas($query, $relation, $constraint){
return $query->whereHas($relation, $constraint)
->with([$relation => $constraint]);
}
的组合(在这里找到答案:https://stackoverflow.com/a/29594039/5297218)。因为两种方法都必须同时满足“ where”条件,所以我在Author模型上使用了query scope:
highest_price
对于额外的transform()
属性,我使用了Collection上的max()
方法,并结合了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();
方法来获得最高的价格。这是最终结果:
Error HRESULT E_FAIL has been returned from a call to a COM component
ps:此解决方案的性能比以前提高了42倍!
答案 0 :(得分:2)
在with()
上的子查询将约束作者与书籍的关系中加载的内容,但不会约束返回的作者的书籍。您是否尝试过whereHas()
而不是with()
?那只会返回拥有符合该标准的书的作者。一个查询就可以做到这一点,但这将取决于您所需要的一切。字段highest_price
的添加可能会更好地调整为其他关系。
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');
如果您有任何疑问,请告诉我。
答案 1 :(得分:0)
使用关系条件时有两种不同的结果:
1-根据条件(您正在执行的操作,而不是您想要的操作)限制RELATIONSHIP结果:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NET461</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>TRACE;DEBUG;NET461</DefineConstants>
</PropertyGroup>
在这里您将拥有所有作者,无论他们是否出版过书籍。那些拥有出版书籍的人将获得书籍信息。所有其他人都不会。
2-根据关系条件限制PARENT结果(这是您应该使用的条件):
$authors = Author::with(['books' => function($query) {
$query->whereNotNull('published');
}])->get();
在这里,只有作者出版过书籍。
注意:如果您希望加载Book模型,则仍然必须使用-> with('books')
答案 2 :(得分:0)
从问题的结果集中,下面应该可以为您提供所需的信息:
Author::whereNotNull('highest_price')
->has('books')
->with(['books' => function($query) use($startDate, $endDate){
return $query->where('category', 'comedy')
->whereBetween('created_at', [$startDate, $endDate])
->orderBy('price', 'desc')
->select('id', 'name', 'category', 'price', 'author_id');
}])
->orderBy('highest_price', 'desc')
->select('id', 'name', 'highest_price')
->get()
说明:
解决这些问题的简单方法是首先构造父查询构建器,在这种情况下为作者。因此,根据您的要求,我们检查了“最高价格”状态,过滤了至少有1本书的作者,并按“最高价格”描述进行排序,并从“作者”表中选择了需要的列。
现在,调查急切的加载,我们进行了相同的操作,过滤了created_at日期范围,按价格desc排序并选择了所需的列。我故意添加了额外的列author_id,这是外键,以便显示书籍的结果。