【问题标题】:Getting just the latest value on a joined table with Eloquent使用 Eloquent 获取连接表的最新值
【发布时间】:2014-08-12 04:24:32
【问题描述】:

我有两张这样的表:

产品:

+----+-----------+
| id |   name    |
+----+-----------+
|  1 | Product 1 |
|  2 | Product 2 |
|  3 | Product 3 |
|  4 | Product 4 |
+----+-----------+

价格:

+----+-------+------------+---------------------+
| id | price | product_id |     created_at      |
+----+-------+------------+---------------------+
|  1 |    20 |          1 | 2014-06-21 16:00:00 |
|  2 |    10 |          1 | 2014-06-21 17:00:00 |
|  3 |    50 |          2 | 2014-06-21 18:00:00 |
|  4 |    40 |          2 | 2014-06-21 19:00:00 |
+----+-------+------------+---------------------+

我在产品上有这种关系:

public function prices()
{
    return $this->hasMany('Price');
}

我可以轻松地运行Product::with('prices')->get(); 以获取每种产品的价格。

如何使用 Eloquent 仅获取最新价格? (另外,如果我想要最便宜/最贵的价格呢?)

【问题讨论】:

    标签: laravel orm eloquent


    【解决方案1】:

    当 Laravel 急切加载一个关系时,它会执行两个类似的查询:

    SELECT * FROM products WHERE 1;
    SELECT * FROM prices WHERE product_id = 1;
    

    您要做的是向第二个查询添加一个条件,以获取具有最新价格的行。所以你会想要这样的东西:

    SELECT * FROM products WHERE 1;
    SELECT * FROM prices WHERE product_id = 1 ORDER BY price;
    

    幸运的是,在 Laravel 的 Eager Load Constraints 中,您可以传递一个数组,其中关系名称作为键,子查询闭包作为其值,而不是将字符串传递给 with()。像这样:

    $products = Product::with(array('prices' => function($query)
    {
        $query->orderBy('created_at', 'desc');
    }))->get();
    

    然后在你的代码中你可以这样做:

    $product->prices->first();
    

    获取每种产品的最新价格。

    注意:你可能会注意到 Laravel 仍然会加载每个产品的所有价格。我认为在仍然使用纯 Eloquent 的同时没有办法解决它,因为急切加载工作的方式是在一个查询中获取所有关系记录,所以没有一种简单的方法可以说只获取每个查询的最新价格产品。


    另一种解决方案:

    但是,如果您只需要知道另一个表中的值,则可以改为进行子选择:

    $products = Product::select('*')
        ->addSelect(DB::raw('(SELECT price FROM prices WHERE products.id = prices.product_id ORDER BY created_at DESC LIMIT 1) price'))
        ->get();
    

    【讨论】:

    • 或者加入他们。 Product::join('prices', 'prices.product_id', '=', 'products.id')->orderBy('prices.somefield', 'asc/desc')->first();
    • 有没有办法让它用于查询一组产品?
    • 是的,只需像往常一样添加一个 where 子句。 ->where('products.field', 'condition', 'value')
    • 是的,这就是重点。该问题明确要求最新的行,最昂贵的等 - 因此first() 调用。编辑:也许我误读了这个问题。如果 OP 意味着获取所有产品但每个产品只有最新价格,那么我的查询确实不起作用。 :)
    • 嗯,我明白你的意思了。我对这个问题的解释不同。
    【解决方案2】:

    你可以调整你的关系来得到你想要的。接受的答案当然有效,但它可能是大量数据的内存过度杀伤。

    了解更多herethere

    以下是如何使用 Eloquent:

    // Product model
    public function latestPrice()
    {
       return $this->hasOne('Price')->latest();
    }
    
    // now we're fetching only single row, thus create single object, per product:
    $products = Product::with('latestPrice')->get();
    $products->first()->latestPrice; // Price model
    

    这很好,但还有更多。想象一下,您想为所有产品加载最高价格(只是一个值):

    public function highestPrice()
    {
       return $this->hasOne('Price')
          ->selectRaw('product_id, max(price) as aggregate')
          ->groupBy('product_id');
    }
    

    还不是很方便:

    $products = Product::with('highestPrice')->get();
    $products->first()->highestPrice; // Price model, but only with 2 properties
    $products->first()->highestPrice->aggregate; // highest price we need
    

    所以添加这个访问器让生活更轻松:

    public function getHighestPriceAttribute()
    {
        if ( ! array_key_exists('highestPrice', $this->relations)) $this->load('highestPrice');
    
        $related = $this->getRelation('highestPrice');
    
        return ($related) ? $related->aggregate : null;
    }
    
    // now it's getting pretty simple
    $products->first()->highestPrice; // highest price value we need
    

    【讨论】:

    • 通过重新定义关系本身来做到这一点。这很整洁。投票!
    • 这真的很棒,我学到了很多东西。我在哪里可以找到有关“最新()”的文档?我检查了laravel.com/api/class-Illuminate.Database.Eloquent.Model.html,在那里看不到任何对它的引用。
    • 这是基本查询生成器上的方法。在这里查看:laravel.com/api/4.2Illuminate\Database\Query\Builder 类上。
    • 太棒了 - 一定是在看旧文档。如果您有时间,当我有一个连接表时,我该如何使用它 - 我还有一个位置表和一个 products_locations 表。 Products 与位置具有 belongsToMany 关系。要获取最新的位置,添加 hasOne('Location')->latest() 不起作用 - 是否容易解决?
    • 多对多关系没有这么简单的方法。我唯一建议的是调整访问器以便轻松访问集合中的first 模型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2021-10-03
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    相关资源
    最近更新 更多