En Laravel, si realizo una consulta:
$foods = Food::where(...)->get();
... entonces $foods
es una colección Illuminate de Food
objetos modelo. (Esencialmente una variedad de modelos).
Sin embargo, las claves de esta matriz son simplemente:
[0, 1, 2, 3, ...]
... así que si quiero alterar, digamos, el Food
objeto con un id
24, no puedo hacer esto:
$desired_object = $foods->get(24);
$desired_object->color = 'Green';
$desired_object->save();
... porque esto simplemente alterará el elemento 25 de la matriz, no el elemento con un valor id
de 24.
¿Cómo obtengo un elemento (s) único (o múltiple) de una colección por CUALQUIER atributo / columna (como, entre otros, id / color / age / etc.)?
Por supuesto, puedo hacer esto:
foreach ($foods as $food) {
if ($food->id == 24) {
$desired_object = $food;
break;
}
}
$desired_object->color = 'Green';
$desired_object->save();
... pero eso es asqueroso.
Y, por supuesto, puedo hacer esto:
$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();
... pero eso es aún más asqueroso , porque realiza una consulta adicional innecesaria cuando ya tengo el objeto deseado en la $foods
colección.
Gracias de antemano por cualquier orientación.
EDITAR:
Para ser claros, puede llamar ->find()
a una Colección Illuminate sin generar otra consulta, pero solo acepta una ID principal. Por ejemplo:
$foods = Food::all();
$desired_food = $foods->find(21); // Grab the food with an ID of 21
Sin embargo, todavía no hay una forma limpia (sin bucles, sin consultas) de tomar un elemento (s) por un atributo de una colección, como este:
$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work. :(
filter()->first()
, solo puede llamarfirst(function(...))
collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
$desired_object = $food->where('id', 24)->first();
Laravel proporciona un método llamado
keyBy
que permite establecer claves por clave dada en el modelo.$collection = $collection->keyBy('id');
devolverá la colección pero con claves como valores de
id
atributo de cualquier modelo.Entonces puedes decir:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
y tomará el elemento correcto sin el lío de usar una función de filtro.
fuente
$exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;
y usar->get($category->id . ' ' . $manufacturer->id)
después!keyBy
devuelve una nueva colección de lo que recuerdo, aunque no estoy seguro, puede verificarloIlluminate/Support/Collection
para averiguarlo. (No trabajo en Laravel durante bastante tiempo para que alguien pueda corregirme).A partir de Laravel 5.5 puedes usar firstWhere ()
En tu caso:
$green_foods = $foods->firstWhere('color', 'green');
fuente
Como no necesito repetir la colección completa, creo que es mejor tener una función de ayuda como esta
/** * 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; }
fuente
Utilice los métodos de colección incorporados contener y buscar , que buscarán por ID primarios (en lugar de claves de matriz). Ejemplo:
if ($model->collection->contains($primaryId)) { var_dump($model->collection->find($primaryId); }
contiene () en realidad solo llama a find () y comprueba si hay nulo, por lo que puede acortarlo a:
if ($myModel = $model->collection->find($primaryId)) { var_dump($myModel); }
fuente
Sé que esta pregunta se hizo originalmente antes de que se lanzara Laravel 5.0, pero a partir de Laravel 5.0, las colecciones admiten el
where()
método para este propósito.Para Laravel 5.0, 5.1 y 5.2, el
where()
método en elCollection
solo hará una comparación igual. Además, hace una comparación estricta de iguales (===
) de forma predeterminada. Para hacer una comparación suelta (==
), puede pasarfalse
como tercer parámetro o usar elwhereLoose()
método.A partir de Laravel 5.3, el
where()
método se amplió para que funcione más como elwhere()
método del generador de consultas, que acepta un operador como segundo parámetro. También al igual que el generador de consultas, el operador utilizará por defecto una comparación igual si no se proporciona ninguna. La comparación predeterminada también se cambió de estricta por defecto a suelta por defecto. Por lo tanto, si desea una comparación estricta, puede usarwhereStrict()
o simplemente usar===
como operador parawhere()
.Por lo tanto, a partir de Laravel 5.0, el último ejemplo de código en la pregunta funcionará exactamente como se esperaba:
$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');
fuente
Debo señalar que hay un error pequeño pero absolutamente CRÍTICO en la respuesta de kalley. Luché con esto durante varias horas antes de darme cuenta:
Dentro de la función, lo que está devolviendo es una comparación y, por lo tanto, algo como esto sería más correcto:
$desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first();
fuente
foreach()
ejemplo en cuanto al rendimiento, porque simplemente hace el mismo tipo de bucle ... de hecho, miforeach()
ejemplo tiene un mejor rendimiento porque se rompe al encontrar el modelo correcto. Además ...{Collection}->find(24)
tomará por clave principal, lo que la convierte en la mejor opción aquí. El filtro propuesto por Kalley es en realidad idéntico$desired_object = $foods->find(24);
.**==**
operador, ¿qué hace?Solución elegante para encontrar un valor ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in -a-collection / ) se puede adaptar:
$desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; }
fuente
Como en la pregunta anterior, cuando utiliza la cláusula where, también debe utilizar el método get Or first para obtener el resultado.
/** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get();
fuente