Getting just the latest value on a joined table with Eloquent

后端 未结 2 1390
自闭症患者
自闭症患者 2020-11-29 07:31

I have two tables like this:

products:

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


        
相关标签:
2条回答
  • 2020-11-29 08:13

    You can tweak your relations to get what you want. Accepted answer of course works, however it might be memory overkill with lots of data.

    Find out more here and there.

    Here's how to use Eloquent for this:

    // 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
    

    That's nice, but there's more. Imagine you'd like to load highest price (just a value) for all the products:

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

    Not very convenient yet:

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

    So add this accessor to make the life easier:

    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
    
    0 讨论(0)
  • 2020-11-29 08:26

    When Laravel eager loads a relationship, it will perform two queries similar to this:

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

    What you want to do is to add a condition to the second query to get the row with most recent price. So you would want something like this:

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

    Luckily in Laravel's Eager Load Constraints you can, instead of passing a string into with(), you can pass an array with the relationship name as key and a subquery closure as its value. Like this:

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

    Then in your code you can do:

    $product->prices->first();
    

    to get the most recent price of each product.

    Note: You may notice that Laravel will still load all the prices for each product. I don't think there's a way around it while still using purely Eloquent because the way eager loading work is fetching all the relationship records in one single query, so there isn't an easy way to say get only the most recent price for each product.


    Another solution:

    However, if you are strictly needing to know just a value from another table, you could do a sub-select instead:

    $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();
    
    0 讨论(0)
提交回复
热议问题