Eloquent with nested whereHas(Eloquent 嵌套 whereHas)
问题描述
目前我在我的模型集合中有这个 whereHas:
$query = self::whereHas('club', function($q) use ($search){$q->whereHas('owner', function($q) use ($search){$q->where('name', 'LIKE', '%'. $search .'%');});});
我的印象是上面的代码可能是这样的:
$query = self::whereHas('club.owner', function($q) use ($search){$q->where('name', 'LIKE', '%'. $search .'%');});
我知道这已经很强大了,但即便如此,如果我有一个 5 层深的嵌套关系,事情就会变得很糟糕.
更新:
正如评论中所述,我最终没有说清楚我的问题,我深表歉意.
我将尝试使用一个简单的例子,考虑$owner->club->membership->product->package
,现在从所有者我想搜索某个包,应该是这样的:
$query = self::whereHas('club', function($q) use ($search){$q->whereHas('membership', function($q) use ($search){$q->whereHas('product', function($q) use ($search){$q->whereHas('package', function($q) use ($search){$q->where('alias', 'LIKE', '%'. $search .'%');});//包裹});//产品});//会员资格});//俱乐部
这是正确的吗?有捷径吗?
更新:PR 刚刚合并到 4.2,所以现在可以在 点嵌套 表示法>has
方法 ( ->has('relation1.relation2)
->whereHas('relation1.relation2, ..
)
您的问题仍然有点不清楚,或者您误解了 whereHas() 方法,因为它用于过滤模型(在本例中为用户)并仅获取那些具有符合搜索条件的相关模型的模型.>
看来您想从给定的用户的上下文中查找包,所以不需要使用 whereHas 方法.
无论如何,取决于关系(1-1,1-m,m-m),这可能很容易,也可能非常困难,而且效率不高.正如我所说,加载嵌套关系意味着对于每一级嵌套都会出现另一个 db 查询,因此在这种情况下,您最终会得到 5 个查询.
无论关系如何,您都可以像这样反转此链,因为这样会更容易:
<小时>这不会在 atm 工作,因为 whereHas() 不处理点嵌套关系!
//给定 $user 和 $search:$packages = Package::where('alias','like',"%$search%")->whereHas('product.membership.club.user', function ($q) use ($user) {$q->whereId($user->id);})->get();
<小时>
如您所见,这更具可读性,但仍运行 5 个查询.通过这种方式,您可以获得 $packages,这是您想要的模型的单个集合.
虽然从用户的上下文中你会得到这样的东西(再次取决于关系):
$user|-俱乐部||-会员|||-产品||||-包|||-另一个产品||||-包|||-又一个产品|||-包||-另一个会员.....
你明白了,不是吗?
您可以从 Collection 中获取包,但这会很麻烦.反过来更容易.
所以你的问题的答案就是加入表格:
//让我们假设关系是最容易处理的:1-many$packages = Package::where('alias','like',"%$search%")->join('products','packages.product_id','=','products.id')->join('memberships','products.membership_id','=','memberships.id')->join('clubs','memberships.club_id','=','clubs.id')->where('clubs.user_id','=',$user->id)->get(['packages.*']);//除了包表外不要选择任何东西
当然你可以用一个很好的方法包装它,这样你就不必每次执行这样的搜索时都写这个.这个查询的性能肯定会比上面显示的单独 5 个查询好得多.显然这种方式你只加载包,没有其他相关的模型.
Currently I have this whereHas in a collection of my model:
$query = self::whereHas('club', function($q) use ($search)
{
$q->whereHas('owner', function($q) use ($search)
{
$q->where('name', 'LIKE', '%'. $search .'%');
});
});
I was under the impression the code above could be as such:
$query = self::whereHas('club.owner', function($q) use ($search)
{
$q->where('name', 'LIKE', '%'. $search .'%');
});
I'm aware this is already a lot of power, but even then, if I have a nested relationship 5 levels deep, things will get ugly.
Update:
As stated in the comments, I ended up not making my question clear, I apologize.
I will try to use a simple example, consider $owner->club->membership->product->package
, now from owners I want to search a certain package, it would be something like this:
$query = self::whereHas('club', function($q) use ($search)
{
$q->whereHas('membership', function($q) use ($search)
{
$q->whereHas('product', function($q) use ($search)
{
$q->whereHas('package', function($q) use ($search)
{
$q->where('alias', 'LIKE', '%'. $search .'%');
});//package
});//product
});//membership
});//club
Is this correct? Is there a shortcut?
Update: the PR has been just merged to 4.2, so now it's possible to use dot nested notation in has
methods ( ->has('relation1.relation2)
->whereHas('relation1.relation2, ..
)
Your question remains a bit unclear or you misunderstand whereHas() method as it is used to filter models (users in this case) and get only those that have related models fitting search conditions.
It seems that you want to find Packages from the context of a given User, so no need to use whereHas method.
Anyway depending on the relations (1-1,1-m,m-m) this can be easy or pretty hard and not very efficient. As I stated, loading nested relations means that for every level of nesting comes another db query, so in this case you end up with 5 queries.
Regardless of the relations you can invert this chain like this, as it will be easier:
edit: This is not going to work atm as whereHas() doesn't process dot nested relations!
// given $user and $search:
$packages = Package::where('alias','like',"%$search%")
->whereHas('product.membership.club.user', function ($q) use ($user) {
$q->whereId($user->id);
})->get();
As you can see this is much more readable, still runs 5 queries. Also this way you get $packages, which is a single Collection of the models you wanted.
While from the context of a user you would get something like this (depending on the relations again):
$user
|-club
| |-membership
| | |-product
| | | |-packages
| | |-anotherProduct
| | | |-packages
| | |-yetAnotherProduct
| | |-packages
| |-anotherMembership
.....
You get the point, don't you?
You could fetch the packages from the Collection, but it would be cumbersome. It's easier the other way around.
So the answer to your question would be simply joining the tables:
// Let's assume the relations are the easiest to handle: 1-many
$packages = Package::where('alias','like',"%$search%")
->join('products','packages.product_id','=','products.id')
->join('memberships','products.membership_id','=','memberships.id')
->join('clubs','memberships.club_id','=','clubs.id')
->where('clubs.user_id','=',$user->id)
->get(['packages.*']); // dont select anything but packages table
Of course you can wrap it in a nice method so you don't have to write this everytime you perform such search. Performance of this query will be definitely much better than separate 5 queries shown above. Obviously this way you load only packages, without other related models.
这篇关于Eloquent 嵌套 whereHas的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:Eloquent 嵌套 whereHas
基础教程推荐
- 使用 PDO 转义列名 2021-01-01
- 如何在 XAMPP 上启用 mysqli? 2021-01-01
- 在 CakePHP 2.0 中使用 Html Helper 时未定义的变量 2021-01-01
- HTTP 与 FTP 上传 2021-01-01
- 找不到类“AppHttpControllersDB",我也无法使用新模型 2022-01-01
- PHP 守护进程/worker 环境 2022-01-01
- 在 yii2 中迁移时出现异常“找不到驱动程序" 2022-01-01
- phpmyadmin 错误“#1062 - 密钥 1 的重复条目‘1’" 2022-01-01
- 如何在 Symfony 和 Doctrine 中实现多对多和一对多? 2022-01-01
- Doctrine 2 - 在多对多关系中记录更改 2022-01-01