Laravel: Получить объект из коллекции по атрибуту

В Laravel, если я выполню запрос:

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

… тогда $foods Food – это иллюминативная коллекция объектов модели Food . (По сути, это набор моделей.)

Однако ключи этого массива просто:

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

… поэтому, если я хочу изменить, скажем, объект Food с id 24, я не могу этого сделать:

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

… потому что это просто изменит 25-й элемент в массиве, а не элемент с id 24.

Как получить один (или несколько) элемент (ы) из коллекции с помощью ANY атрибута / столбца (например, id / color / age / и т. Д.)?

Конечно, я могу это сделать:

 foreach ($foods as $food) { if ($food->id == 24) { $desired_object = $food; break; } } $desired_object->color = 'Green'; $desired_object->save(); 

… но это просто грубо.

И, конечно, я могу это сделать:

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

… но это еще более грубо , потому что он выполняет дополнительный ненужный запрос, когда у меня уже есть нужный объект в коллекции $foods .

Заранее благодарю за любое руководство.

РЕДАКТИРОВАТЬ:

Чтобы быть ясным, вы можете вызывать ->find() в коллекции Illuminate, не порождая другого запроса, но он принимает только первичный идентификатор. Например:

 $foods = Food::all(); $desired_food = $foods->find(21); // Grab the food with an ID of 21 

Тем не менее, до сих пор нет чистого (нециклирующего, не запрашивающего) способа захвата элемента (ов) атрибутом из коллекции, например:

 $foods = Food::all(); $green_foods = $foods->where('color', 'green'); // This won't work. :( 

Вы можете использовать filter , например:

 $desired_object = $food->filter(function($item) { return $item->id == 24; })->first(); 

filter также вернет Collection , но, поскольку вы знаете, что будет только один, вы можете позвонить first в эту Collection .

Вам больше не нужен фильтр (или, может быть, когда-либо, я не знаю, что это почти 4 года). Вы можете использовать first :

 $desired_object = $food->first(function($item) { return $item->id == 24; }); 

Laravel предоставляет метод keyBy который позволяет устанавливать ключи с помощью заданного ключа в модели.

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

вернет коллекцию, но с ключами будут значения атрибута id из любой модели.

Тогда вы можете сказать:

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

и он будет захватывать правильный элемент без беспорядка использования функции фильтра.

Поскольку мне не нужна целая коллекция, я думаю, что лучше иметь вспомогательную функцию, подобную этой

 /** * 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; } 

Используйте встроенные методы сбора и поиска , которые будут искать по основным идентификаторам (вместо ключей массива). Пример:

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

contains () фактически просто вызывает find () и проверяет значение null, поэтому вы можете сократить его до:

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

Я знаю, что этот вопрос был первоначально задан до того, как был выпущен Laravel 5.0, но с Laravel 5.0 Collections поддерживает метод where() для этой цели.

Для Laravel 5.0, 5.1 и 5.2 метод where() в Collection будет выполнять только сравнительное сравнение. Кроме того, по умолчанию выполняется строгое сравнение ( === ). Чтобы выполнить свободное сравнение ( == ), вы можете передать значение false в качестве третьего параметра или использовать метод whereLoose() .

Начиная с Laravel 5.3, метод where() был расширен, чтобы больше работать как метод where() для построителя запросов, который принимает оператор в качестве второго параметра. Также, как и построитель запросов, оператор будет по умолчанию равным сравнению, если ни один не будет предоставлен. Сравнение по умолчанию также было отключено от строкового по умолчанию, чтобы потерять по умолчанию. Итак, если вы хотите строгое сравнение, вы можете использовать whereStrict() или просто использовать === как оператор для where() .

Поэтому, начиная с Laravel 5.0, последний пример кода в вопросе будет работать точно так, как предполагалось:

 $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'); 

Я должен указать, что в ответе Калли есть небольшая, но абсолютно критическая ошибка. Я боролся с этим несколько часов, прежде чем осознать:

Внутри функции то, что вы возвращаете, является сравнением, и, следовательно, что-то вроде этого было бы более правильным:

 $desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first(); 

Элегантное решение для определения ценности ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in -a-collection / ) можно адаптировать:

 $desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; } 

В качестве вопроса выше, когда вы используете предложение where, вам также нужно использовать метод get или first для получения результата.

 /** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get();