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 在评论中提到的:对 withwhereHas 应用相同的约束函数:

$constraint = function ($query) use ($phone_uuid) {
    $query->where('uuid', $phone_uuid);
};

Driver::with(['provider.driverPhones' => $constraint])
    ->whereHas('provider.driverPhones', $constraint)
    ->get();