php - rematime - colgantes a pedido
Laravel: Obtener Object From Collection por atributo (8)
En Laravel, si realizo una consulta:
$foods = Food::where(...)->get();
... entonces $foods
es un objeto Illuminate Collection of Food
model. (Esencialmente una matriz de modelos).
Sin embargo, las claves de esta matriz son simplemente:
[0, 1, 2, 3, ...]
... así que si quiero alterar, por ejemplo, el objeto Food
con una 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 una id
de 24.
¿Cómo obtengo un elemento único (o múltiple) de una colección por CUALQUIER atributo / columna (como, pero no limitado a, 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 innecesaria adicional cuando ya tengo el objeto deseado en la colección $foods
.
Gracias de antemano por cualquier orientación.
EDITAR:
Para que quede claro, puede llamar a ->find()
en 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 agarrar 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. :(
Como la pregunta anterior cuando usa la cláusula where, también necesita usar 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();
Como no es necesario que bucle toda la colección, creo que es mejor tener la función auxiliar como este
/**
* 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;
}
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ás devolviendo es una comparación, y por lo tanto, algo así sería más correcto:
$desired_object = $food->filter(function($item) {
return ($item->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 las claves siendo los valores del atributo id
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 desorden de usar una función de filtro.
Puede usar el filter
, así:
$desired_object = $food->filter(function($item) {
return $item->id == 24;
})->first();
filter
también devolverá una Collection
, pero como sabes que solo habrá una, puedes llamar first
a esa Collection
.
Ya no necesita el filtro (o tal vez nunca, no sé si tiene casi 4 años). Puedes usar first
:
$desired_object = $food->first(function($item) {
return $item->id == 24;
});
Sé que esta pregunta se hizo originalmente antes de que se lanzara Laravel 5.0, pero a partir de Laravel 5.0, Collections admite el método where()
para este fin.
Para Laravel 5.0, 5.1 y 5.2, el método where()
en la Collection
solo hará una comparación igual. Además, hace una comparación igual estricta ( ===
) por defecto. Para hacer una comparación suelta ( ==
), puede pasar false
como el tercer parámetro o usar el método whereLoose()
.
A partir de Laravel 5.3, el método where()
se expandió para funcionar más como el método where()
para el generador de consultas, que acepta un operador como el segundo parámetro. También al igual que el generador de consultas, el operador establecerá de manera predeterminada una comparación igual si no se proporciona ninguna. La comparación predeterminada también cambió de estricta por defecto a suelta por defecto. Entonces, si desea una comparación estricta, puede usar whereStrict()
, o simplemente usar ===
como el 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'');
Una 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];
}
Utilice los métodos de recopilación incorporados que contienen y encuentran , que buscarán por identificadores primarios (en lugar de claves de matriz). Ejemplo:
if ($model->collection->contains($primaryId)) {
var_dump($model->collection->find($primaryId);
}
contains () realmente solo llama a find () y busca nulo, por lo que puedes acortarlo a:
if ($myModel = $model->collection->find($primaryId)) {
var_dump($myModel);
}