Eloquent:最新相关模型符合许可的模型范围

Eloquent: Scope for Model where latest related Model meets permission

我有一个模型产品,其中包含许多模型价格。 每天都有不同的产品价格。

现在我正在尝试创建一个范围,为我提供最新价格介于两个值之间的所有产品。

我用 whereHas 查询试过了:

public function scopePriceBetween($query, ...$priceRange) {
  return $query->whereHas('price', function ($query) use ($priceRange) {
     $query->latestPrice()->whereBetween('price', $priceRange); 
  })
}

价格模型的范围

public function scopeLatestPrice($query) {
  return $query->latest('date')->limit(1);
}

但这会给我所有价格在范围内的产品,而不仅仅是最新价格。

有没有办法在 eloquent 中以可接受的性能做到这一点,或者我是否需要向我的产品模型添加 latest_price 列?

对于以后的价格,您可以使用数据库临时列,也可以使用 redis。但我推荐临时专栏。

第一个解决方案:临时 Table

DB::statement("CREATE TEMPORARY TABLE last_prices SELECT prices.* from prices join products on products.id=prices.product_id and prices.id=(select id from prices where prices.product_id=products.id and `prices`.`deleted_at` is null order by `id` desc limit 1);");
        $query = Product::select("products.*")
                 ->join("last_prices", "products.id", "last_prices.product_id");
        

在此示例中,每个任务都有很多作业,您可以查询数据库以创建临时 table 并从 jobs;

中获取 last_job

第二种解决方案:使用缓存服务器

DBMS temp table 很快,但您可以通过缓存服务器(例如 redis)获得性能。

您可以通过 product_id:

将每个产品的最后价格存储在缓存服务器中
  public function getLastPriceAttribute(){
    
    //cache for an hour
    $p_id = $this->id;
    return  Cache::tags(['product'])->remember($this->id, 60*60, function () uses ($p_id) {
            return Price::where('product_id', $p_id)
                ->latest()
                ->first();
        });  
}

方案三:

如果您的价格每天更新并且您没有或不想使用缓存服务器,您可以创建一个名为 last_prices 的数据库 table 并使用 [=52= 每天更新它] 安排如下:

App\Console\Kernel.php 中:

//suggestion has not tested
protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            $updateValues = array();
            
            foreach( Product::all() as $product){
               array_push($updateValues , array(
                   "product_id" => product->id,
                   "price_value" => 
                   Price::where('product_id',$product->id)
                   ->latest()
                   ->first()->price_value;
                   ));
             }
        LastPrices::updateOrInsert($updateValues);
        })->dailyAt("05:30");        }

更新

为此:

Product::latestPriceBetween([100,200])->category('electronics');

您可以使 建议的第三个解决方案 具有 Last_price Table.

并使用 join 定义范围,使用这个漂亮的包:https://github.com/fico7489/laravel-eloquent-join

看起来像这样:

public function scopePriceBetween($query, ...$priceRange) {
  return $query->join("last_prices","last_prices.product_id","products.id")->whereBetween('last_prices.value', $priceRange);
}