Laravel 在 Blade 模板中使用 whereHas 查询子项

huangapple go评论67阅读模式
英文:

Laravel whereHas children in Blade template

问题

我目前拥有产品和时段的概念作为模型;一个时段是一个时间段,一个产品可以拥有一个或多个时段。

也就是说,一个产品可以拥有多个时段,举个例子如下:

产品 - "烹饪课"
时段 - ['2023-12-12 11:00:00', '2023-12-12 13:00:00']

我目前查询的方式如下:

Product::whereHas('slots', function ($query) {
    $query->whereDate('start_time', '=', $this->booking_date);
})->get();

这个查询按预期工作,但在我的 Blade 模板中,当我想要列出可用的时段时,调用

{{$product->slots()}}

会根据模型中定义的关系获取所有时段,并不会注意到 WhereDate 子句 - 在我的 Blade 模板中,如何迭代给定产品的时段,并考虑到时段的 WhereDate 子句呢?理想情况下,我不想再为此编写另一个查询。

英文:

I currently have the concept of Products and Slots as Models; A Slot is a time slot, and a product can have 1 or many time slots.

That is to say that One Product can have many Slots. lay example being as below:

Product - "Cookery Class"
Slots - ['2023-12-12 11:00:00', '2023-12-12 13:00:00']

I currently query this like below:

Product::whereHas('slots' ,function ($query) {
        $query->whereDate('start_time', '=', $this->booking_date);
    })->get();

This works as expected, however inside my Blade template when I want to list the slots available calling

{{$product->slots()}}

Get's all slots as per the relationship defined in the Model and takes no notice of the WhereDate clause - how can I within my Blade template iterate through the slots for a given Product with the WhereDate clause of Slots. Ideally I don't want to write another query for this.

答案1

得分: 1

->whereHas()改为->withWhereHas()

$products = Product::withWhereHas('slots', function ($query) {
  $query->whereDate('start_time', '=', $this->booking_date);
})->get(); 

这将"急切加载" slots 模型,并过滤您的 Product 记录,只保留满足嵌套的 ->whereDate() 子句的记录。

接下来,在您的 .blade.php 文件中,不要使用 ->slots()。这有两个问题:

  1. 带括号的 ->slots() 总是一个新的查询,完全忽略了通过 ->withWhereHas() 进行的急切加载。
  2. ->slots() 的查询实际上没有执行,因此执行 {{ $product->slots() }} 很可能什么都不会输出,或者如您所述,输出所有 slot 记录,即使它们不满足您指定的 ->whereDate 子句。

它应该是这样的:

@foreach($product->slots()->get() as $slot)
  {{ $slot }}
@endforeach

但再次强调,您不应该这样做,因为现在存在 N+1 查询问题,每次迭代 $products 都会执行一个新的查询,还有上述提到的其他问题。

您最终的 .blade.php 代码应该是这样的:

@foreach($products as $product)
  @foreach($product->slots as $slot)
    {{ $slot }}
  @endforeach
@endforeach

https://laravel.com/docs/10.x/eloquent-relationships#constraining-eager-loads-with-relationship-existence

英文:

Change ->whereHas() to ->withWhereHas():

$products = Product::withWhereHas('slots' ,function ($query) {
  $query->whereDate('start_time', '=', $this->booking_date);
})->get(); 

This will "Eager Load" the slots Models, as well as filter your Product records to only those that satisfy the nested ->whereDate() clause.

Next, in your .blade.php file, don't do ->slots(). There's 2 issues with that:

  1. ->slots() with () is always a new query, and completely ignores the eager loading done via ->withWhereHas()
  2. The query from ->slots() isn't actually executed, so doing {{ $product->slots() }} will likely output nothing, or, as you stated, all slot records, even those that do not satisfy the ->whereDate clause you specified.

It would need to be:

@foreach($product->slots()->get() as $slot)
  {{ $slot }}
@endforeach

But again, you wouldn't want to do this, as it's now an N+1 query issue, where each iteration of $products executes a new query, along with the other issues mentioned above.

Your final .blade.php code should be:

@foreach($products as $product)
  @foreach($product->slots as $slot)
    {{ $slot }}
  @endforeach
@endforeach

https://laravel.com/docs/10.x/eloquent-relationships#constraining-eager-loads-with-relationship-existence

huangapple
  • 本文由 发表于 2023年7月28日 02:56:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76782684.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定