【问题标题】:Laravel `with` issuesLaravel `with` 问题
【发布时间】:2017-11-29 11:18:05
【问题描述】:

我有 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);
    }

}

现在 - 我想获取所有可用的 Drivers,其 Provider DriverPhones 符合给定标准。我认为这样做是可行的方法:

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 是怎么回事?

【问题讨论】:

  • 尝试使用with()whereHas() 的组合,它们使用相同的子查询$query->where('uuid', $phone_uuid);,看看是否返回所需的查询结果。
  • @TimLewis - 对不起,我不明白;我以为我使用的是相同的子查询
  • 您的查询应该是Driver::with(...)->whereHas(...)->get();->with()->whereHas() 使用相同的子查询。我在您的问题中没有看到这一点;只有一个使用->with() 的实例和一个使用->whereHas() 的实例。也许将您的最终查询编辑到问题中,我可能读错了。
  • @TimLewis - 我的印象是with 包含所有关系(使用连接),因此只运行一个查询(然后根据一个查询的结果构建关系模型),而 whereHas 执行子查询,有效地运行了两个。
  • 一开始我也是这么想的,但是每当我想用whereHas() 约束我的原始查询并包含->with() 的关系时,我不得不同时使用两者来完成这个.试一试,使用dd() 检查结果

标签: php laravel eloquent laravel-5.3 eager-loading


【解决方案1】:

首先让我建议一种更好的方法来检查您的 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 子句。因此它从结果中“过滤”,但不提供相关模型的即时加载。

所以解决方案就是 Tim Lewis 在 cmets 中提到的:对 withwhereHas 应用相同的约束函数:

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

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

【讨论】:

  • 非常好 - 非常感谢您采用这种检查查询的方式。看来我的Coaches 设置了错误的外键,这是罪魁祸首(whereHas= 进行检查,3129 = 0101AwithIN 检查哪个不返回true)。
猜你喜欢
  • 1970-01-01
  • 2014-10-16
  • 2018-12-14
  • 2020-08-01
  • 2022-09-26
  • 2020-07-13
  • 2022-12-07
  • 2019-05-29
  • 2011-08-02
相关资源
最近更新 更多