Laravel: Get Object From Collection By Attribute
In Laravel, if I perform a query:
$foods = Food::where(...)->get();
...then $foods
is an Illuminate Collection of Food
model objects. (Essentially an array of models.)
However, the keys of this array are simply:
[0, 1, 2, 3, ...]
...so if I want to alter, say, the Food
object with an id
of 24, I can't do this:
$desired_object = $foods->get(24);
$desired_object->color = 'Green';
$desired_object->save();
...because this will merely alter the 25th element in the array, not the element with an id
of 24.
How do I get a single (or multiple) element(s) from a collection by ANY attribute/column (such as, but not limited to, id / color / age / etc.)?
Of course, I can do this:
foreach ($foods as $food) {
if ($food->id == 24) {
$desired_object = $food;
break;
}
}
$desired_object->color = 'Green';
$desired_object->save();
...but, that's just gross.
And, of course, I can do this:
$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();
...but that's even more gross, because it performs an additional unnecessary query when I already have the desired object in the $foods
collection.
Thanks in advance for any guidance.
EDIT:
To be clear, you can call ->find()
on an Illuminate Collection without spawning another query, but it only accepts a primary ID. For instance:
$foods = Food::all();
$desired_food = $foods->find(21); // Grab the food with an ID of 21
However, there is still no clean (non-looping, non-querying) way to grab an element(s) by an attribute from a Collection, like this:
$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work. :(
Solution 1:
You can use filter
, like so:
$desired_object = $food->filter(function($item) {
return $item->id == 24;
})->first();
filter
will also return a Collection
, but since you know there will be only one, you can call first
on that Collection
.
You don't need the filter anymore (or maybe ever, I don't know this is almost 4 years old). You can just use first
:
$desired_object = $food->first(function($item) {
return $item->id == 24;
});
Solution 2:
Laravel provides a method called keyBy
which allows to set keys by given key in model.
$collection = $collection->keyBy('id');
will return the collection but with keys being the values of id
attribute from any model.
Then you can say:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
and it will grab the correct item without the mess of using a filter function.
Solution 3:
As from Laravel 5.5 you can use firstWhere()
In you case:
$green_foods = $foods->firstWhere('color', 'green');