largo
En Laravel, si realizo una consulta:
$foods = Food::where(...)->get();
…después $foods
es un Colección iluminar de Food
modelar objetos. (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
de 24, no puedo hacer esto:
$desired_object = $foods->get(24);
$desired_object->color="Green";
$desired_object->save();
… porque esto simplemente alterará el elemento 25 en la matriz, no el elemento con un id
de 24
¿Cómo obtengo uno o varios elementos de una colección por CUALQUIER atributo/columna (como, entre otros, id/color/edad/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 asquerosoporque realiza una consulta adicional innecesaria cuando ya tengo el objeto deseado en el $foods
recopilación.
EDITAR:
Para ser claro, usted lata llamada ->find()
en una Colección Illuminate sin generar otra consulta, pero solamente acepta una identificación 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 capturar un elemento (s) por un atributo de una colección, como esta:
$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work. :(
calle
Puedes usar filter
al igual que:
$desired_object = $food->filter(function($item) {
return $item->id == 24;
})->first();
filter
también devolverá un Collection
pero como sabes que solo habrá uno, puedes llamar first
en ese Collection
.
Ya no necesitas el filtro (o tal vez nunca, no sé esto tiene casi 4 años). solo puedes usar first
:
$desired_object = $food->first(function($item) {
return $item->id == 24;
});
-
¡Hey gracias! Creo que puedo vivir con eso. Todavía inusualmente detallado en mi opinión para lo que suele ser un marco tan ‘Elocuente’ jaja. Pero sigue siendo mucho más limpio que las alternativas hasta ahora, así que lo tomaré.
– Longitud
5 de enero de 2014 a las 7:09
-
Como @squaretastic señala en la otra respuesta, dentro de su cierre está haciendo una asignación y no una comparación (es decir, debe == y no =)
– Tormenta Elemental
9 de junio de 2014 a las 12:57
-
En realidad ni siquiera es necesario llamar
filter()->first()
solo puedes llamarfirst(function(...))
– lukasgeiter
04/02/2015 a las 20:10
-
de la documentación de la Colección Laravel. laravel.com/docs/5.5/colecciones#método-primero
collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
– Shiro
12 dic 2017 a las 15:25
-
Puedes hacer lo mismo con la función where.
$desired_object = $food->where('id', 24)->first();
– Bhavin Thummar
28 de agosto de 2018 a las 11:02
Maksim
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 siendo los 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.
-
Realmente útil, especialmente para el rendimiento, ->first() puede ser lento cuando se llama varias veces (foreach en foreach…) para que pueda “indexar” su colección como:
$exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;
y use->get($category->id . ' ' . $manufacturer->id)
después !– François Bretón
25 de abril de 2016 a las 16:32
-
¿Se sigue utilizando esta clave cuando se añaden nuevos elementos a la colección? ¿O necesito usar keyBy() cada vez que se inserta un nuevo objeto o matriz en la colección?
– Jasón
22 de septiembre de 2016 a las 14:22
-
Lo más probable es que tengas que llamarlo de nuevo ya que
keyBy
devuelve una nueva colección de lo que recuerdo, aunque no estoy seguro, puedes verificarIlluminate/Support/Collection
para averiguarlo (No estoy trabajando en Laravel desde hace bastante tiempo, así que alguien puede corregirme).– Maksim
04/12/2016 a las 20:44
-
Esto no funcionó para mí, devolvió otro elemento, el siguiente elemento, si escribo obtener (1) devolverá el elemento que tiene el número 2 como identificación.
– Jaqueline Passos
21 de enero de 2017 a las 16:42
-
Lote cargando una mesa y tardó un día. Usé esta solución y tomó minutos.
–Jed Lynch
27 de abril de 2019 a las 4:29
A partir de Laravel 5.5 puedes usar primeroDónde()
En tu caso:
$green_foods = $foods->firstWhere('color', 'green');
-
Esta debería ser la respuesta aceptada después de Laravel 5.5
– Beerwin
13 de marzo de 2019 a las 16:13
Utilice los métodos de recopilación incorporados Contiene y encontrar, que buscará por identificadores 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 verifica si es nulo, por lo que podría acortarlo a:
if ($myModel = $model->collection->find($primaryId)) {
var_dump($myModel);
}
Como no necesito recorrer toda la colección, creo que es mejor tener una función auxiliar 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;
}
patricio
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 el Collection
solo hará una comparación de iguales. Además, hace una comparación estricta de igualdad (===
) por defecto. Para hacer una comparación suelta (==
), puede pasar false
como el tercer parámetro o utilizar el whereLoose()
método.
A partir de Laravel 5.3, el where()
método se amplió para trabajar más como el where()
para el generador de consultas, que acepta un operador como segundo parámetro. También al igual que el generador de consultas, el operador utilizará de forma predeterminada 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. Entonces, si desea una comparación estricta, puede usar whereStrict()
o simplemente usar ===
como operador de where()
.
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');
rana blanda
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];
}