Laravel: Получить Объект Из Коллекции По Атрибуту


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

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

...тогда $foods - это Коллекция подсветки Food объектов модели. (По сути, массив моделей.)

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

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

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

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

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

Как мне получите один (или несколько) элементов (ов) из коллекции по ЛЮБОМУ атрибуту/столбцу (например, но не ограничиваясь этим, идентификатор/цвет/возраст/и т. Д.)?

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

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.  :(
Author: Leng, 2014-01-05

8 answers

Вы можете использовать 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;
});
 68
Author: kalley, 2017-12-12 15:58:18

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

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

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

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

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

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

 71
Author: Maksym Cierzniak, 2015-06-23 19:50:24

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

/**
 * 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
Author: Rohith Raveendran, 2014-08-14 06:48:31

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

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

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

if ($myModel = $model->collection->find($primaryId)) {
    var_dump($myModel);
}
 4
Author: Ziad Hilal, 2015-01-12 21:56:47

Я знаю, что этот вопрос был первоначально задан до выпуска Laravel 5.0, но начиная с Laravel 5.0 коллекции поддерживают метод 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');
 4
Author: patricus, 2017-06-03 02:27:24

Я должен отметить, что в ответе Калли есть небольшая, но абсолютно КРИТИЧЕСКАЯ ошибка. Я боролся с этим в течение нескольких часов, прежде чем понял:

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

$desired_object = $food->filter(function($item) {
    return ($item->id **==** 24);
})->first();
 3
Author: squaretastic, 2014-05-15 23:29:42

Элегантное решение для поиска значения (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];
}
 1
Author: softfrog, 2014-09-30 10:19:26

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

/**
*Get all food
*
*/

$foods = Food::all();

/**
*Get green food 
*
*/

$green_foods = Food::where('color', 'green')->get();
 -1
Author: Marco, 2017-10-18 08:35:18