Laravel雄辩的“ with()”渴望加载和“ whereHas()”条件

时间:2019-02-14 17:01:44

标签: laravel eloquent query-builder laravel-5.7 laravel-collection

我有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件事:

  1. 从日期{1}和日期2之间的authors 喜剧books中获取category中具有created_at的所有highest_price的列表。
  2. 列表中的作者应具有一个额外的属性 price,这是与步骤1中的条件相匹配的所有作者书籍中的最高价格。
  3. 书籍列表应按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倍!

3 个答案:

答案 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,这是外键,以便显示书籍的结果。