使用Eloquent获取联接表上的最新值

时间:2014-06-21 16:46:16

标签: laravel orm eloquent

我有两张这样的表:

产品:

+----+-----------+
| 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才能获得最新价格? (另外,如果我想要最便宜/最昂贵的价格呢?)

2 个答案:

答案 0 :(得分:23)

你可以调整你的关系以获得你想要的东西。接受的答案当然有效,但是可能存在大量数据的内存过多。

了解更多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

答案 1 :(得分:3)

当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();