急切负载约束和 whereHas

Posted

技术标签:

【中文标题】急切负载约束和 whereHas【英文标题】:Eager load constraint and whereHas 【发布时间】:2014-04-20 21:23:06 【问题描述】:

考虑这个查询:

$query = Ticket::whereHas('user', function($q) use ($search)

    $q->where(function($q) use ($search)
    
        $q->where('name', 'LIKE', '%'. $search .'%')->orWhere('username', 'LIKE', '%'. $search .'%');
    );
)->paginate(10);

还有这个:

$query = Ticket::with(array('user' => function($q) use ($search)

    $q->where('name', 'LIKE', '%'. $search .'%')->orWhere('username', 'LIKE', '%'. $search .'%');
))->paginate(10);

在测试期间,第一个查询在搜索 John 时返回了 50 张票中的 47 张,正如预期的那样。

用第二个查询复制搜索,返回 50 个结果,全部返回。

更准确地说,第二个查询中发生的情况是,当不应该返回的 3 行之一在分页中时,它会在尝试访问诸如 $ticket->user->name 之类的属性时中断。否则,什么都不会中断。

为什么第二个查询会失败?

【问题讨论】:

你想做什么? 我想根据上面的like子句检索属于某个用户的票。 你有一个工作的,第一个。 我对另一个很好奇。 【参考方案1】:

在下面给出的第二个中:

$query = Ticket::with(array('user' => function($q) use ($search) 
    $q->where('name', 'LIKE', '%'. $search .'%')->orWhere('username', 'LIKE', '%'. $search .'%');
))->paginate(10);

你得到所有的票是因为你没有过滤票,例如,这样想查询:

$query = Ticket::with('user')->paginate(10);

你会得到什么,所有有用户的票,但是通过添加一个约束,你只过滤用户而不是票,所以在这种情况下,你的查询将是这样的:

select * from `tickets` where `tickets`.`deleted_at` is null
select * from `users` where `users`.`ticket_id` in (?, ?, ?, ?, ?, ?, ?, ?) and `name` ... ?

因此,首先选择票证时没有任何过滤,然后对于用户,将执行另一个查询,但在第一个查询中执行的是不同的查询。因此,在第二个示例中,由于您的所有票证都没有user,因此当在不包含相关用户的Ticket 模型上执行$ticket->user->name 时,错误就会出现,因为没有@ 987654327@ 所以没有name 属性。

第一个查询 (whereHas) 可能如下所示:

select * from `tickets` where `tickets`.`deleted_at` is null and (select count(*) from `users` where `users`.`ticket_id` = `tickets`.`id` and `name` LIKE ? ...) >= ?

如果您想查看每个生成的查询,请在所有查询之后使用以下查询:

dd(DB::getQueryLog());

这将返回一个由查询生成器生成并执行的查询数组。

【讨论】:

DB::getQueryLog()和你说的一模一样。非常感谢您对这位狼人先生的说明。

以上是关于急切负载约束和 whereHas的主要内容,如果未能解决你的问题,请参考以下文章

约束急切负载的自定义 orderBy()?

Laravel 中的急切负载约束过滤器问题

带有约束的 Laravel 嵌套急切加载

Laravel 约束急切加载 - 如果关系列应用限制!= 1

Eloquent:急切加载选择约束?可能?

如何在 Laravel 中为 ORM 急切加载添加多个键约束?