You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Eloquent中如何在JOIN里使用Scope获取带有效价格的商品

问题描述

我希望通过ProductPrices模型中的Scope获取所有带有有效价格的商品,并同时展示商品及其有效价格。我的Product模型与价格是hasMany关联:

Product.php(模型)

public function prices () {
  return $this->hasMany(ProductsPrice::class);
}

我的ProductsPrices模型中有一个isActive Scope,用于判断当前日期下价格是否有效:

ProductsPrices.php(模型)

public function scopeIsActive($query)
{
  return $query->whereRaw(' timestampdiff(second, start_time, NOW()) >= 0')
               ->where(function ($query) {
                  $query->whereRaw(' timestampdiff(second, end_time, NOW()) <= 0')
                     ->orWhereNull('end_time');
                });
} 

我尝试了多种方法但均未成功,以下是报错的写法:

第一种写法

Route::get('/test', function (Request $request) {
  return Product::join('products_prices', 'products.id', 'products_prices.product_id')
      ->prices->isActive()
      ->where('products.is_active', true)
      ->get();
});

报错信息:

Property [prices] does not exist on the Eloquent builder instance.

第二种写法(test2)

Route::get('/test2', function (Request $request) {
  $prices = DB::table('products_prices')->select('id');
  $product = Product::whereIn('id', $prices)->get();

  return $product->prices()->isActive()->get();
});

报错信息:

Method Illuminate\Database\Eloquent\Collection::prices does not exist.

疑问:为何无法在Product模型上调用->prices()方法?是否应该放弃Eloquent改用Laravel查询构造器?


问题分析与解决方案

报错原因

  1. 第一种写法错误:Product::join(...)返回的是Eloquent查询构建器实例,而prices是模型定义的关联方法,只能在单个Product模型实例上调用,不能直接在查询构建器上用属性访问的方式调用。
  2. 第二种写法错误:Product::whereIn(...)->get()返回的是Eloquent集合(Collection),集合是多个模型实例的集合,不能直接调用prices()关联方法,必须遍历集合中的单个模型实例才能调用。

正确实现方式

不需要放弃Eloquent,有几种优雅的方式实现需求:

方式1:使用with关联预加载+筛选有效价格

通过with()方法预加载关联,并在关联中应用isActive Scope,同时筛选出至少有一个有效价格的商品:

Route::get('/test', function (Request $request) {
    return Product::where('is_active', true)
        ->whereHas('prices', function ($query) {
            // 筛选出存在有效价格的商品
            $query->isActive();
        })
        ->with(['prices' => function ($query) {
            // 预加载时只加载有效价格
            $query->isActive();
        }])
        ->get();
});
  • whereHas:确保商品至少存在一个符合条件的价格记录
  • with:预加载关联时只获取有效价格,避免N+1查询问题

方式2:从价格模型反向查询

如果更关注价格数据,也可以从ProductsPrice模型出发,获取有效价格并关联对应的商品:

Route::get('/test', function (Request $request) {
    return ProductsPrice::isActive()
        ->whereHas('product', function ($query) {
            $query->where('is_active', true);
        })
        ->with('product')
        ->get();
});

需要先在ProductsPrice模型中定义反向关联:

public function product() {
    return $this->belongsTo(Product::class);
}

方式3:自定义查询构造器关联查询

如果需要自定义返回字段,也可以直接用查询构造器关联表查询:

Route::get('/test', function (Request $request) {
    return Product::where('is_active', true)
        ->join('products_prices', 'products.id', '=', 'products_prices.product_id')
        ->whereRaw('timestampdiff(second, products_prices.start_time, NOW()) >= 0')
        ->where(function ($query) {
            $query->whereRaw('timestampdiff(second, products_prices.end_time, NOW()) <= 0')
                  ->orWhereNull('products_prices.end_time');
        })
        ->select('products.*', 'products_prices.*') // 按需选择需要返回的字段
        ->get();
});

注意:如果一个商品有多个有效价格,这种方式会返回多条对应记录,可根据需求调整。

关键知识点

  • prices()是单个模型实例的方法:仅能在单个Product对象上调用,例如$product = Product::find(1); $product->prices()->isActive()->get();
  • 查询构建器(Product::where(...))和集合(get()返回的结果)都不是单个模型实例,因此无法直接调用关联方法

内容的提问来源于stack exchange,提问作者St. Jan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 05:35:41