laravel 在透视表中查找最小列值

o4tp2gmn  于 2023-03-04  发布在  其他
关注(0)|答案(1)|浏览(152)

我有两个模型。ProductVendor。这些模型有多对多的关系。透视表有额外的列旁边product_idvendor_idprice and off_price所以表列如下:
| 产品标识|供应商标识|价格|减价|
| - ------|- ------|- ------|- ------|
我在Product模型中编写了自定义属性:

protected $appends = [
        'cheapest_vendor'
    ];

    protected function CheapestVendor(): Attribute
    {
        $price = $this->belongsToMany(Vendor::class)->min('price');
        $vendor = $this->belongsToMany(Vendor::class)->where('price', '=', $price)->first();
        return new Attribute(
            get: fn () => ['price' => $price, 'vendor' => $vendor],
        );
    }

在我的刀锋档案里

$product->cheapest_vendor->price and $product->cheapest_vendor->vendor->name

多次。问题是我有这么多的重复查询的刀片式服务器页,因为这一点。我知道每次我调用cheapest_vendor属性,我实际上运行2查询,但我想不出其他方法来做到这一点。我需要一种方法来找到最低价格的透视表和供应商的产品没有这么多的重复查询。

11dmarpk

11dmarpk1#

您的问题肯定是在Attribute方法中同时包含$price = $this->belongsToMany(Vendor::class)$vendor = $this->belongsToMany(Vendor::class)。如果您在那里定义它们,则无法检查该Relationship是否已加载,因此每次访问$product->cheapest_vendor时都将调用2 Queries。
您 * 可以 * 通过与vendors的单个关系来完成此操作:

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

下一步分为两部分:
1.确保“属性”检查加载的关系并使用它(如果已定义)或加载它:

protected function CheapestVendor(): Attribute {
  if ($this->relationLoaded('vendors') {
    $this->load('vendors', function ($subQuery) {
      return $subQuery->orderBy('price', 'ASC');
    });
  }

  $cheapestVendor = $this->vendors->first();

  return new Attribute(
    get: fn () => ['price' => $cheapestVendor->price, 'vendor' => $cheapestVendor],
  );
}

对于单个模型,这非常有效。您可以这样做:

// In your Controller: 
$product = Product::first();

// In your View:
{{ $product->cheapest_vendor->price }}

{{ $product->cheapest_vendor->vendor->name }}

并且只会调用一个额外的查询。
1.对于多个模型,请确保在继续之前正确加载关系:

$products = Product::with(['vendors' => function ($subQuery) { 
  return $subQuery->orderBy('price', 'ASC');
})->get();

现在,当循环这些产品并显示其cheapest_vendor属性时,将不执行任何其他查询:

@foreach($products as $product)
  {{ $product->cheapest_vendor->price }}
  {{ $product->cheapest_vendor->vendor->name }}
@endforeach

如果拥有2个关系(1个与vendors,一个与cheapest_vendors)更容易,您也可以这样做:

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

public function cheapest_vendors() {
  return $this->belongsToMany(Vendor::class, 'vendor_id')
  ->withPivot(['price', 'off_price'])
  ->orderBy('price', 'ASC');
}
  • 注意:这仍然需要是belongsToMany(),因为它们通过pivot表连接 *

现在,您可以简化您的逻辑:

protected function CheapestVendor(): Attribute {
  if ($this->relationLoaded('cheapest_vendors') {
    $this->load('cheapest_vendors');
  }

  $cheapestVendor = $this->cheapest_vendors->first();

  return new Attribute(
    get: fn () => ['price' => $cheapestVendor->price, 'vendor' => $cheapestVendor],
  );
}

加载$product(或$products)时,在控制器中:

$product = Product::with('cheapest_vendors')->first();

// OR

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

当访问$product->cheapest_vendor->price(和->vendor->name)时,将不执行任何其他查询。这还允许您访问->vendors,它将返回所有关联的供应商,而在单一关系方法中,->vendors将仅包括单个Vendor模型示例的集合。

相关问题