Laravel`有`问题
Laravel `with` issues
我有 3 个这样定义的模型:
class Driver extends Model
{
public function provider(){
return $this->belongsTo(Provider::class);
}
}
class DriverPhone extends Model
{
public function provider(){
return $this->belongsTo(Provider::class);
}
}
class Provider extends Model
{
public function drivers(){
return $this->hasMany(Driver::class);
}
public function driverPhones(){
return $this->hasMany(DriverPhone::class);
}
}
现在 - 我想获取所有可用的 Driver
,其 Provider
DriverPhone
满足给定条件。我认为这样做是可行的方法:
Driver::with(['provider.driverPhones' => function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
}]);
当我通过以下方式分析查询时:
var_dump($phone_uuid, Driver::with(['provider.driverPhones' => function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
}])->toSql()); exit;
它给了我:
select * from `drivers` where `drivers`.`deleted_at` is null
然而,当我使用 whereHas
时,结果集是正确的:
Driver::whereHas('provider.driverPhones', function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
})->get()`
我是不是漏掉了什么?
后续问题 - 对于给定的其他模型:
class Journey extends Model
{
public function coach(){
return $this->belongsTo(CoachId::class);
}
}
class Coach extends Model
{
public function journey(){
return $this->belongsTo(Journey::class);
}
}
如果我搜索 Journey::with('coach', 'journeyLocations')->whereHas('coach', [my subquery])
,它会找到正确的 Journey
,但尝试访问 coach
时会返回 null。如果我省略了with
,访问的是正确的coach
属性,当然多个查询是运行.
那么 - with
是怎么回事?
首先让我建议一个更好的方法来检查您的 sql 查询:
- 在查询前添加
\DB::listen(function ($q) { \Log::info($q->sql, $q->bindings); });
。
- 在命令行中,运行
tail -f storage/logs/laravel.log
查看执行时打印的查询。
你会发现实际上 with
并没有做 JOIN。相反,它会查找具有 WHERE 子句的相关模型:WHERE id IN (1, 2, 3)
。这不是联接,而是单独的查询。
另一方面,whereHas
确实在同一个查询中包含约束,带有 WHERE EXISTS 子句。所以它 "filters" 从结果来看,但不提供相关模型的预加载。
所以解决方案就是 Tim Lewis 在评论中提到的:对 with
和 whereHas
应用相同的约束函数:
$constraint = function ($query) use ($phone_uuid) {
$query->where('uuid', $phone_uuid);
};
Driver::with(['provider.driverPhones' => $constraint])
->whereHas('provider.driverPhones', $constraint)
->get();
我有 3 个这样定义的模型:
class Driver extends Model
{
public function provider(){
return $this->belongsTo(Provider::class);
}
}
class DriverPhone extends Model
{
public function provider(){
return $this->belongsTo(Provider::class);
}
}
class Provider extends Model
{
public function drivers(){
return $this->hasMany(Driver::class);
}
public function driverPhones(){
return $this->hasMany(DriverPhone::class);
}
}
现在 - 我想获取所有可用的 Driver
,其 Provider
DriverPhone
满足给定条件。我认为这样做是可行的方法:
Driver::with(['provider.driverPhones' => function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
}]);
当我通过以下方式分析查询时:
var_dump($phone_uuid, Driver::with(['provider.driverPhones' => function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
}])->toSql()); exit;
它给了我:
select * from `drivers` where `drivers`.`deleted_at` is null
然而,当我使用 whereHas
时,结果集是正确的:
Driver::whereHas('provider.driverPhones', function($query) use ($phone_uuid){
$query->where('uuid', $phone_uuid);
})->get()`
我是不是漏掉了什么?
后续问题 - 对于给定的其他模型:
class Journey extends Model
{
public function coach(){
return $this->belongsTo(CoachId::class);
}
}
class Coach extends Model
{
public function journey(){
return $this->belongsTo(Journey::class);
}
}
如果我搜索 Journey::with('coach', 'journeyLocations')->whereHas('coach', [my subquery])
,它会找到正确的 Journey
,但尝试访问 coach
时会返回 null。如果我省略了with
,访问的是正确的coach
属性,当然多个查询是运行.
那么 - with
是怎么回事?
首先让我建议一个更好的方法来检查您的 sql 查询:
- 在查询前添加
\DB::listen(function ($q) { \Log::info($q->sql, $q->bindings); });
。 - 在命令行中,运行
tail -f storage/logs/laravel.log
查看执行时打印的查询。
你会发现实际上 with
并没有做 JOIN。相反,它会查找具有 WHERE 子句的相关模型:WHERE id IN (1, 2, 3)
。这不是联接,而是单独的查询。
另一方面,whereHas
确实在同一个查询中包含约束,带有 WHERE EXISTS 子句。所以它 "filters" 从结果来看,但不提供相关模型的预加载。
所以解决方案就是 Tim Lewis 在评论中提到的:对 with
和 whereHas
应用相同的约束函数:
$constraint = function ($query) use ($phone_uuid) {
$query->where('uuid', $phone_uuid);
};
Driver::with(['provider.driverPhones' => $constraint])
->whereHas('provider.driverPhones', $constraint)
->get();