2014-06-21 10 views
8

Ho due tabelle come questo:Ottenere solo l'ultimo valore su un tavolo unito con Eloquente

prodotti:

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

prezzi:

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

ho questo relazione su Prodotto:

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

Posso facilmente eseguire Product::with('prices')->get(); per ottenere ogni prodotto con ciascuno dei prezzi che ha avuto.

Come posso utilizzare Eloquent per ottenere solo il prezzo più recente? (Inoltre, cosa succede se volevo che il prezzo più basso/più costosa, invece?)

risposta

21

Puoi modifica le tue relazioni per ottenere quello che vuoi. La risposta accettata naturalmente funziona, tuttavia potrebbe trattarsi di un sovraccarico di memoria con molti dati.

Ulteriori informazioni here e there.

Ecco come utilizzare eloquente per questo:

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

Che bello, ma c'è di più. Immaginate di desideri caricare prezzo più alto (solo un valore) per tutti i prodotti:

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

molto conveniente ancora:

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

Quindi aggiungere questo di accesso per rendere la vita più facile:

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

Fatelo ridefinendo la relazione stessa. È abbastanza carino. Votare! – Unnawut

+0

Questo è davvero fantastico, e ho imparato molto. Dove posso trovare la documentazione su 'latest()'? Ho controllato http://laravel.com/api/class-Illuminate.Database.Eloquent.Model.html e non ho visto alcun riferimento ad esso. –

+0

È il metodo sul generatore di query di base. Controlla qui: http://laravel.com/api/4.2/ sulla classe 'Illuminate \ Database \ Query \ Builder'. –

3

Quando laravel carichi desiderosi di un rapporto, eseguirà due interrogazioni simile a questo:

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

Che cosa si vuole fare è quello di aggiungi una condizione alla seconda query per ottenere la riga con il prezzo più recente. Così si vorrebbe qualcosa di simile:

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

Per fortuna in laravel di Eager Load Constraints è possibile, invece di passare una stringa in with(), è possibile passare un array con il nome della relazione come chiave e una chiusura subquery come valore. Come questo:

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

Poi nel codice che si può fare:

$product->prices->first(); 

per conoscere il costo più recente di ciascun prodotto.

Nota: Si può notare che laravel sarà ancora caricare tutti i prezzi per ogni prodotto. Non penso che ci sia un modo per aggirarlo mentre si usa puramente Eloquent perché il modo in cui il caricamento ansioso sta recuperando tutti i record delle relazioni in una singola query, quindi non c'è un modo semplice per dire di ottenere solo il prezzo più recente per ciascuno Prodotto.


Un'altra soluzione:

Tuttavia, se si sta strettamente bisogno di sapere solo un valore da un'altra tabella, si potrebbe fare un sub-selezionare invece:

$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(); 
+1

Oppure unisciti a loro. '' Product :: join ('prezzi', 'prices.product_id', '=', 'products.id') -> orderBy ('prices.somefield', 'asc/desc') -> first(); ' ' –

+0

C'è un modo per farlo funzionare per interrogare un insieme di prodotti? – Unnawut

+0

Sì, basta aggiungere una clausola where normalmente. '' -> where ('products.field', 'condition', 'value') '' ecc. –

Problemi correlati