W Laravel, jeśli wykonam zapytanie:
$foods = Food::where(...)->get();
... wtedy $foods
jest Illuminate Collection of Food
modelowych obiektów. (Zasadniczo szereg modeli.)
Jednak klucze tej tablicy to po prostu:
[0, 1, 2, 3, ...]
... więc jeśli chcę zmienić, powiedzmy, Food
obiekt z id
wartością 24, nie mogę tego zrobić:
$desired_object = $foods->get(24);
$desired_object->color = 'Green';
$desired_object->save();
... ponieważ to tylko zmieni 25-ty element tablicy, a nie element z id
wartością 24.
Jak uzyskać pojedynczy (lub wiele) element (y) z kolekcji według DOWOLNEGO atrybutu / kolumny (takiego jak, ale nie wyłącznie, identyfikator / kolor / wiek / itp.)?
Oczywiście mogę to zrobić:
foreach ($foods as $food) {
if ($food->id == 24) {
$desired_object = $food;
break;
}
}
$desired_object->color = 'Green';
$desired_object->save();
... ale to po prostu obrzydliwe.
I oczywiście mogę to zrobić:
$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();
... ale to jeszcze bardziej obrzydliwe , ponieważ wykonuje dodatkowe niepotrzebne zapytanie, gdy mam już żądany obiekt w $foods
kolekcji.
Z góry dziękuję za wszelkie wskazówki.
EDYTOWAĆ:
Żeby było jasne, to może zadzwonić ->find()
na Kolekcji Illuminate bez tarła kolejne zapytanie, ale tylko akceptuje podstawowy identyfikator. Na przykład:
$foods = Food::all();
$desired_food = $foods->find(21); // Grab the food with an ID of 21
Jednak nadal nie ma czystego (nie zapętlającego się i nie wykonującego zapytań) sposobu na pobranie elementu (ów) przez atrybut z kolekcji, na przykład:
$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work. :(
filter()->first()
, możesz po prostu zadzwonićfirst(function(...))
collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
$desired_object = $food->where('id', 24)->first();
Laravel udostępnia metodę o nazwie,
keyBy
która pozwala ustawić klucze według danego klucza w modelu.$collection = $collection->keyBy('id');
zwróci kolekcję, ale z kluczami będącymi wartościami
id
atrybutu z dowolnego modelu.Następnie możesz powiedzieć:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
i złapie właściwy przedmiot bez bałaganu związanego z używaniem funkcji filtra.
źródło
$exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;
i używać->get($category->id . ' ' . $manufacturer->id)
after!keyBy
zwraca nową kolekcję z tego, co pamiętam, chociaż nie jestem pewien, możesz sprawdzić,Illuminate/Support/Collection
aby się dowiedzieć. (Nie pracuję w Laravel od dłuższego czasu, więc ktoś może mnie poprawić).Od Laravel 5.5 możesz użyć firstWhere ()
W twoim przypadku:
$green_foods = $foods->firstWhere('color', 'green');
źródło
Ponieważ nie muszę zapętlać całej kolekcji, myślę, że lepiej jest mieć taką funkcję pomocniczą
/** * Check if there is a item in a collection by given key and value * @param Illuminate\Support\Collection $collection collection in which search is to be made * @param string $key name of key to be checked * @param string $value value of key to be checkied * @return boolean|object false if not found, object if it is found */ function findInCollection(Illuminate\Support\Collection $collection, $key, $value) { foreach ($collection as $item) { if (isset($item->$key) && $item->$key == $value) { return $item; } } return FALSE; }
źródło
Użyj wbudowanych metod kolekcji zawierających i znajduj , które będą wyszukiwać według podstawowych identyfikatorów (zamiast kluczy tablicowych). Przykład:
if ($model->collection->contains($primaryId)) { var_dump($model->collection->find($primaryId); }
zawiera () w rzeczywistości po prostu wywołuje find () i sprawdza wartość null, więc możesz skrócić to do:
if ($myModel = $model->collection->find($primaryId)) { var_dump($myModel); }
źródło
Wiem, że to pytanie zostało pierwotnie zadane przed wydaniem Laravel 5.0, ale od Laravel 5.0 Kolekcje obsługują
where()
metodę do tego celu.W przypadku Laravel 5.0, 5.1 i 5.2
where()
metoda naCollection
woli wykonuje tylko porównanie równości. Ponadto===
domyślnie wykonuje ścisłe porównanie równości ( ). Aby wykonać luźne porównanie (==
), możesz albo przekazaćfalse
jako trzeci parametr, albo użyćwhereLoose()
metody.Od wersji Laravel 5.3
where()
metoda została rozszerzona, aby bardziej przypominaławhere()
metodę konstruktora zapytań, która przyjmuje operator jako drugi parametr. Podobnie jak w konstruktorze zapytań, operator domyślnie wybierze porównanie równości, jeśli żadne nie zostanie podane. Domyślne porównanie zostało również zmienione ze ścisłego domyślnie na luźne. Jeśli więc chcesz uzyskać ścisłe porównanie, możesz użyćwhereStrict()
lub po prostu użyć===
jako operatora dlawhere()
.Dlatego od wersji Laravel 5.0 ostatni przykład kodu w pytaniu będzie działał dokładnie tak, jak powinien:
$foods = Food::all(); $green_foods = $foods->where('color', 'green'); // This will work. :) // This will only work in Laravel 5.3+ $cheap_foods = $foods->where('price', '<', 5); // Assuming "quantity" is an integer... // This will not match any records in 5.0, 5.1, 5.2 due to the default strict comparison. // This will match records just fine in 5.3+ due to the default loose comparison. $dozen_foods = $foods->where('quantity', '12');
źródło
Muszę zaznaczyć, że w odpowiedzi Kalleya jest mały, ale absolutnie KRYTYCZNY błąd. Zmagałem się z tym przez kilka godzin, zanim zdałem sobie sprawę:
Wewnątrz funkcji zwracasz porównanie, więc coś takiego byłoby bardziej poprawne:
$desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first();
źródło
foreach()
przykładu pod względem wydajności, ponieważ po prostu wykonuje ten sam rodzaj pętli ... w rzeczywistości mójforeach()
przykład działa lepiej, ponieważ psuje się po znalezieniu prawidłowego modelu. Ponadto ...{Collection}->find(24)
będzie przechwytywał klucz główny, co czyni go najlepszą opcją tutaj. Filtr zaproponowany przez Kalleya jest w rzeczywistości identyczny z$desired_object = $foods->find(24);
.**==**
operatora, co robi?Eleganckie rozwiązanie do znajdowania wartości ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in -a-kolekcja / ) można dostosować:
$desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; }
źródło
Jak w powyższym pytaniu, gdy używasz klauzuli where, musisz również użyć metody get Or first, aby uzyskać wynik.
/** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get();
źródło