约束急切加载关系

Constraining eager loaded relationship

我发现 with 函数在重载关系时有一个非常奇怪的行为。我有 ProductDeal 关系,这样 Product belongsTo() Deal (通过 product_id in deals table ).现在,当我尝试让所有产品都打折时:

Product::with(['deal' => function($query) {
    $query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
}])->get()

此 returns 所有 产品的集合,即使 没有 条记录 deals table 和 所有 产品的 deal_id 设置为 NULL。同时 Product::has('deal')->get() returns 一个空集合,如你所料。

我最初是在尝试获取五个随机促销产品以及 DealImage 关系时发现此问题的:

Product::with(['deal' => function ($query) {
        $query->whereDate('ends_at', '>', // promo still active
                             Carbon::now()->toDateTimeString());
    },
    'images' => function ($query) {
        $query->where('featured', true);    // image featured on homepage
    }])
->where('status', 'IN_STOCK')   // 'In Stock'
->whereNull('deleted_at')       // wasn't soft-deleted
->orderByRaw('RAND()')
->take(5)->get())

这会生成一个集合,其中包含所有 Product 中的 5 个随机 Product。我尝试了 query->whereNotNull('ends_at')->whereDate('ends_at' ..... ); 但得到了相同的结果。

我做错了什么?

这里你对概念的理解是完全错误的。

如果您说的是 Product belongsTo() Deal,那么让我们假设 Deal hasMany() 产品.

这是特价商品table

deals
id | name | ends_at | blah | blah

products
id | deal_id | name | blah | blah

所以基本上,Product::with('deal') 应该 return 您所有产品的交易都被 Eager 加载。但是 Deal::with('products') 会 return 你一个空的集合,因为没有产品有一个有效的 deal_id

请务必注意,由于 Product 只能 belongTo 一个 Deal,您将始终获得当您执行 Product::with('deal') 查询时,交易模型而不是集合。但是当你执行Deal::with('products')你一定会得到一个集合

所以基本上,当你说

这 return 是所有产品的集合,即使交易 table 中没有记录并且所有产品都 deal_id 设置为 NULL。

很明显...因为这里的查询是针对产品而不是交易进行的。如果您想在 ends_at > Carbon::now() 处找到 Deal,则必须这样做。

Deal::with('product')->where('ends_at', '>', Carbon::now()->toDateTimeString())

当你使用 with 时,它只会根据提供的约束预先加载关系,但如果你想通过它们的关系过滤父模型,那么 whereHas 是你的朋友。所以你的查询应该是:

Product::whereHas('deal' => function($query) {
          $query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
        })->get();

现在它将只获取满足给定约束的 Product

您还可以使用 withwhereHas 的组合作为:

Product::whereHas('deal' => function($query) {
          $query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
        })
        ->with(['deal' => function($query) {
            $query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
        }])
        ->get();