2014-06-21 8 views
8

Mam dwie tabele tak:Uzyskiwanie tylko ostatnią wartość na połączonej tabeli z wymownym

produkty:

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

Ceny:

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

mam to związek na produkt:

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

Mogę łatwo uruchomić Product::with('prices')->get();, aby uzyskać każdy produkt z każdą ceną, jaką miał.

Jak korzystać z Eloquent, aby otrzymywać najświeższe ceny? (Również, co gdybym chciał najtańszy/najdroższą cenę zamiast?)

Odpowiedz

21

Można dostosuj swoje relacje, aby uzyskać to, czego chcesz. Odpowiedź zaakceptowana oczywiście działa, jednak może to być przesada pamięci z dużą ilością danych.

Dowiedz się więcej here i there.

Oto jak korzystać z tego wymowny:

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

To miłe, ale nie więcej. Wyobraź chcesz załadować najwyższą cenę (tylko wartości) dla wszystkich produktów:

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

utrudniona jeszcze:

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

Więc dodać akcesor, aby ułatwić życie:

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

Zrób to, zmieniając samą relację. To całkiem miłe. Głosowanie w górę! – Unnawut

+0

To jest naprawdę wspaniałe i wiele się nauczyłem. Gdzie mogę znaleźć dokumentację dotyczącą 'latest()'? Sprawdziłem http://laravel.com/api/class-Illuminate.Database.Eloquent.Model.html i nie widziałem tam żadnego odnośnika. –

+0

Jest to metoda na podstawie konstruktora zapytań. Sprawdź tutaj: http://laravel.com/api/4.2/ na klasie 'Illuminate \ Database \ Query \ Builder'. –

3

Kiedy laravel chętnych ładunku związek, wykona dwa zapytania podobne do tego:

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

Co chcesz zrobić, to dodaj warunek do drugiego zapytania, aby uzyskać wiersz z ostatnią ceną. Więc chcesz coś takiego:

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

szczęście w laravel na Eager Load Constraints można zamiast przechodzącą ciąg do with(), można przekazać tablicę z nazwą związku jako klucz i zamknięcia podzapytanie jako jego wartość. Tak:

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

Następnie w kodzie można zrobić:

$product->prices->first(); 

aby uzyskać najnowszą cenę każdego produktu.

Uwaga: Możesz zauważyć, że Laravel nadal będzie ładować wszystkie ceny dla każdego produktu. Nie sądzę, że istnieje sposób obejścia tego problemu, przy jednoczesnym korzystaniu z czysto Eloquent, ponieważ sposób, w jaki zachłannie ładuje się dane, polega na pobieraniu wszystkich rekordów relacji w jednym zapytaniu, więc nie ma łatwego sposobu na uzyskanie tylko najnowszej ceny dla każdego z nich. produkt.


Innym rozwiązaniem:

Jednakże, jeśli są ściśle potrzebują wiedzieć, tylko wartości z innej tabeli, można zrobić sub-wybierz zamiast:

$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

Lub dołącz do nich. '' Produkt :: join ('ceny', 'prices.product_id', '=', 'products.id') -> orderBy ('prices.somefield', 'asc/desc') -> first(); ' ' –

+0

Czy jest jednak jakiś sposób, aby działał on na potrzeby sprawdzania zestawu produktów? – Unnawut

+0

Tak, po prostu dodaj klauzul where, tak jak zwykle. '' -> where ('products.field', 'condition', 'value') '' itp. –

Powiązane problemy