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

Laravel多对多关联中优化pivot表最低价格及对应供应商查询

解决多对多关联中重复查询最低价格供应商的问题

核心问题分析

你现在的问题是每次调用cheapest_vendor属性都会触发两次查询,视图里多次调用就会产生大量重复查询——本质是没做实例级缓存,也没利用Laravel的预加载机制避免N+1查询。

方案1:给模型属性加实例级缓存

在Product模型里用私有变量缓存已经查询过的结果,同一产品实例多次调用属性不会重复查库,同时把原来的两次查询合并成一次:

class Product extends Model
{
    // 用私有变量缓存查询结果
    private ?object $cheapestVendor = null;

    public function vendors()
    {
        return $this->belongsToMany(Vendor::class)
            ->withPivot('price', 'off_price');
    }

    public function getCheapestVendorAttribute()
    {
        // 如果已经缓存过,直接返回
        if ($this->cheapestVendor) {
            return $this->cheapestVendor;
        }

        // 一次查询拿到最低价格的供应商,优先用off_price,没有就用price
        $this->cheapestVendor = $this->vendors()
            ->select('vendors.*', 'product_vendor.price', 'product_vendor.off_price')
            ->orderByRaw('LEAST(COALESCE(off_price, price), price) ASC')
            ->first();

        return $this->cheapestVendor;
    }
}

方案2:预加载所有产品的最低供应商(解决批量产品的N+1问题)

如果是一次性查询多个Product,直接在控制器里预加载每个产品的最低供应商数据,彻底避免后续重复查询:

// 控制器里查询Product时,用withSub预计算每个产品的最低供应商
$products = Product::query()
    ->withSub([
        'cheapestVendor' => function ($query) {
            $query->from('vendors')
                ->join('product_vendor', 'vendors.id', '=', 'product_vendor.vendor_id')
                ->whereColumn('product_vendor.product_id', 'products.id')
                ->select('vendors.*', 'product_vendor.price', 'product_vendor.off_price')
                ->orderByRaw('LEAST(COALESCE(off_price, price), price) ASC')
                ->limit(1);
        }
    ])
    ->get();

之后在视图里直接调用$product->cheapestVendor就能拿到预加载好的数据,完全不用再查库。

方案3:自定义关联关系

也可以给Product模型定义一个专门的关联,用来直接获取最低价格的供应商,然后通过预加载使用:

class Product extends Model
{
    public function vendors()
    {
        return $this->belongsToMany(Vendor::class)
            ->withPivot('price', 'off_price');
    }

    // 自定义关联:获取当前产品最低价格的供应商
    public function cheapestVendor()
    {
        return $this->belongsToMany(Vendor::class)
            ->withPivot('price', 'off_price')
            ->orderByRaw('LEAST(COALESCE(off_price, price), price) ASC')
            ->limit(1);
    }
}

控制器里预加载:

$products = Product::with('cheapestVendor')->get();

视图里调用:$product->cheapestVendor->first()(因为是多对多关联,返回的是集合,取第一个就是目标供应商)

内容的提问来源于stack exchange,提问作者Mohsen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:13:25