Laravel: Get an object from a collection by attribute

In Laravel, if I execute a request:

$foods = Food::where(...)->get(); 

... then $foods is an Illuminate Collection of Food model objects. (This is essentially an array of models.)

However, the keys of this array are simple:

 [0, 1, 2, 3, ...] 

... therefore, if I want to change, say, a Food object with an id of 24, I cannot do this:

 $desired_object = $foods->get(24); $desired_object->color = 'Green'; $desired_object->save(); 

... because it just changes the 25th element in the array, not the element with id of 24.

How to get one (or several) items from a collection by ANY attribute / column (for example, 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, it's just rude.

And of course I can do this:

 $desired_object = Food::find(24); $desired_object->color = 'Green'; $desired_object->save(); 

... but this is even more rude because it makes an additional unnecessary request when I already have the desired object in the $foods collection.

Thanks in advance for any recommendations.

EDIT:

To be clear, you can call ->find() in the Illuminate collection without creating another query, but it only accepts the primary identifier. For example:

 $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 capture element (s) with an attribute from the collection, for example:

 $foods = Food::all(); $green_foods = $foods->where('color', 'green'); // This won't work. :( 
+44
php mysql laravel
Jan 05 '14 at 7:00
source share
8 answers

You can use filter , for example:

 $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 this Collection .

You no longer need a filter (or maybe someday, I don’t know that it's almost 4 years). You can simply use first :

 $desired_object = $food->first(function($item) { return $item->id == 24; }); 
+55
Jan 05 '14 at 7:04
source share

Laravel provides a method called keyBy that allows you to set keys using a given key in the model.

$collection = $collection->keyBy('id');

will return the collection, but with the keys there will be id attribute values ​​from any model.

Then you can say:

$desired_food = $foods->get(21); // Grab the food with an ID of 21

and it will capture the correct element without the clutter of using the filter function.

+58
Mar 09 '15 at 9:55
source share

Since I don’t need to quote the entire collection, I think it’s better to have a helper function like this

 /** * 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; } 
+7
Aug 14 '14 at 6:48
source share

Use the built-in collection methods contain and find , which will search by basic identifiers (instead of array keys). Example:

 if ($model->collection->contains($primaryId)) { var_dump($model->collection->find($primaryId); } 

contains () actually just calls find () and checks for null, so you can shorten it to:

 if ($myModel = $model->collection->find($primaryId)) { var_dump($myModel); } 
+4
Jan 12 '15 at 21:56
source share

I know this question was originally asked before Laravel 5.0 was released, but with Laravel 5.0 Collections, the where() method is supported for this purpose.

For Laravel 5.0, 5.1, and 5.2, the where() method on Collection will only perform a comparative comparison. In addition, strict comparisons ( === ) are performed by default. To perform a free comparison ( == ), you can pass false as the third parameter or use the whereLoose() method.

Starting with Laravel 5.3, the where() method has been extended to look more like the where() method for the query builder, which takes an operator as the second parameter. As well as the query builder, the operator will default to a comparison if none are provided. The default comparison has also been disabled from the default string to lose the default. So, if you want a strict comparison, you can use whereStrict() or just use === as the operator for where() .

Therefore, as with Laravel 5.0, the last code sample in the question will work exactly as intended:

 $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'); 
+4
Jun 03 '17 at 2:27
source share

I must point out that Callie's answer has a small but absolutely CRITICAL error. I struggled with this for several hours before realizing:

Inside the function, what you return is a comparison, and therefore something like this would be more correct:

 $desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first(); 
+3
May 15 '14 at 23:29
source share

Elegant solution for finding the value ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in- a-collection / ) can be adapted:

 $desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; } 
+1
Sep 30 '14 at 10:19
source share

As the question above, when you use the where clause, you also need to use the get or first method to get the result.

 /** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get(); 
-one
Oct 18 '17 at 8:35
source share



All Articles