No Laravel, se eu fizer uma consulta:
$foods = Food::where(...)->get();
... então $foods
é uma coleção Illuminate de Food
objetos modelo. (Essencialmente, uma variedade de modelos.)
No entanto, as chaves desta matriz são simplesmente:
[0, 1, 2, 3, ...]
... então, se eu quiser alterar, digamos, o Food
objeto com um id
de 24, não posso fazer isso:
$desired_object = $foods->get(24);
$desired_object->color = 'Green';
$desired_object->save();
... porque isso apenas alterará o 25º elemento na matriz, não o elemento com um id
de 24.
Como obtenho um único (ou vários) elemento (s) de uma coleção por QUALQUER atributo / coluna (como, mas não se limitando a id / cor / idade / etc.)?
Claro, eu posso fazer isso:
foreach ($foods as $food) {
if ($food->id == 24) {
$desired_object = $food;
break;
}
}
$desired_object->color = 'Green';
$desired_object->save();
... mas, isso é simplesmente nojento.
E, claro, posso fazer isso:
$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();
... mas isso é ainda mais grosseiro , porque realiza uma consulta adicional desnecessária quando já tenho o objeto desejado na $foods
coleção.
Desde já, agradeço qualquer indicação.
EDITAR:
Para ser claro, você pode chamar ->find()
uma coleção Illuminate sem gerar outra consulta, mas ela só aceita uma ID primária. Por exemplo:
$foods = Food::all();
$desired_food = $foods->find(21); // Grab the food with an ID of 21
No entanto, ainda não há uma maneira limpa (sem loop, sem consulta) de pegar um (s) elemento (s) por um atributo de uma coleção, como este:
$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work. :(
filter()->first()
você pode apenas ligarfirst(function(...))
collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
$desired_object = $food->where('id', 24)->first();
O Laravel fornece um método chamado
keyBy
que permite definir as chaves por uma determinada chave no modelo.$collection = $collection->keyBy('id');
retornará a coleção, mas com as chaves sendo os valores dos
id
atributos de qualquer modelo.Então você pode dizer:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
e pegará o item correto sem a confusão de usar uma função de filtro.
fonte
$exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;
e usar->get($category->id . ' ' . $manufacturer->id)
depois!keyBy
retorna uma nova coleção do que eu me lembro, mas não tenho certeza, você pode verificarIlluminate/Support/Collection
para descobrir. (Não estou trabalhando no Laravel há algum tempo para que alguém possa me corrigir).A partir do Laravel 5.5 você pode usar firstWhere ()
No seu caso:
$green_foods = $foods->firstWhere('color', 'green');
fonte
Como não preciso fazer um loop de coleção inteira, acho melhor ter uma função 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; }
fonte
Use os métodos de coleção internos contêm e localizam , que pesquisam por ids primários (em vez de chaves de array). Exemplo:
if ($model->collection->contains($primaryId)) { var_dump($model->collection->find($primaryId); }
contains () na verdade apenas chama find () e verifica se há nulo, então você pode encurtá-lo para:
if ($myModel = $model->collection->find($primaryId)) { var_dump($myModel); }
fonte
Eu sei que esta pergunta foi feita originalmente antes do lançamento do Laravel 5.0, mas a partir do Laravel 5.0, as Coleções suportam o
where()
método para este propósito.Para o Laravel 5.0, 5.1 e 5.2, o
where()
método noCollection
fará apenas uma comparação de igual. Além disso, ele faz uma comparação de igual estrita (===
) por padrão. Para fazer uma comparação flexível (==
), você pode passarfalse
como o terceiro parâmetro ou usar owhereLoose()
método.A partir do Laravel 5.3, o
where()
método foi expandido para funcionar mais como owhere()
método do construtor de consultas, que aceita um operador como o segundo parâmetro. Também como o construtor de consultas, o operador assumirá como padrão uma comparação igual se nenhuma for fornecida. A comparação padrão também foi alterada de estrita por padrão para flexível por padrão. Portanto, se desejar uma comparação estrita, você pode usarwhereStrict()
, ou apenas usar===
como o operador parawhere()
.Portanto, a partir do Laravel 5.0, o último exemplo de código na questão funcionará exatamente como pretendido:
$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');
fonte
Devo salientar que há um pequeno erro, mas absolutamente CRÍTICO na resposta de Kalley. Lutei com isso por várias horas antes de perceber:
Dentro da função, o que você está retornando é uma comparação e, portanto, algo assim seria mais correto:
$desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first();
fonte
foreach()
exemplo em termos de desempenho, porque ele apenas faz o mesmo tipo de loop ... na verdade, meuforeach()
exemplo tem melhor desempenho porque quebra ao encontrar o modelo correto. Além disso ...{Collection}->find(24)
será capturado pela chave primária, o que o torna a melhor opção aqui. O filtro proposto por Kalley é realmente idêntico$desired_object = $foods->find(24);
.**==**
operadora, o que ela faz?Solução elegante para encontrar um valor ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in -a-coleção / ) pode ser adaptado:
$desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; }
fonte
Como a pergunta acima, quando você está usando a cláusula where, você também precisa usar o método get Or first para obter o resultado.
/** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get();
fonte