web-dev-qa-db-fra.com

Laravel: Obtenir un objet de la collection par attribut

À Laravel, si j'effectue une requête:

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

... then $foods est un Illuminate Collection of Food objets modèle. (Essentiellement un tableau de modèles.)

Cependant, les clés de ce tableau sont simplement:

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

... donc si je veux modifier, disons, l'objet Food avec une id de 24, je ne peux pas le faire:

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

... parce que cela modifiera simplement le 25ème élément du tableau, et non l'élément avec un id de 24.

Comment puis-je obtenir un seul (ou plusieurs) élément (s) d'une collection par TOUS les attributs/colonnes (tels que, sans s'y limiter, id/color/age/etc.)?

Bien sûr, je peux faire ceci:

foreach ($foods as $food) {
    if ($food->id == 24) {
        $desired_object = $food;
        break;
    }
}
$desired_object->color = 'Green';
$desired_object->save();

... mais, c'est juste brut.

Et bien sûr, je peux le faire:

$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();

... mais c'est encore plus brut, car il effectue une requête inutile supplémentaire lorsque j'ai déjà l'objet souhaité dans la collection $foods.

Merci d'avance pour toute orientation.

MODIFIER:

Pour être clair, vous pouvez appeler ->find() sur une collection Illuminate sans générer une autre requête, mais il uniquement accepte un ID principal. Par exemple:

$foods = Food::all();
$desired_food = $foods->find(21);  // Grab the food with an ID of 21

Cependant, il n'y a toujours pas de moyen propre (sans boucle ni requête) de saisir un ou plusieurs éléments par un attribut d'une collection, comme ceci:

$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work.  :(
54
Leng

Vous pouvez utiliser filter , comme suit:

$desired_object = $food->filter(function($item) {
    return $item->id == 24;
})->first();

filter retournera également un Collection, mais comme vous savez qu'il n'y en aura qu'un, vous pouvez appeler first sur cette Collection.

Vous n'avez plus besoin du filtre (ou peut-être jamais, je ne sais pas s'il a presque 4 ans). Vous pouvez simplement utiliser first :

$desired_object = $food->first(function($item) {
    return $item->id == 24;
});
78
kalley

Comme je n'ai pas besoin de boucler toute la collection, je pense qu'il est préférable d'avoir une fonction d'aide comme celle-ci. 

/**
 * 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
Rohith Raveendran

Utilisez les méthodes de collecte intégrées Container et find , qui effectuera une recherche par ID primaire (au lieu de clés de tableau). Exemple:

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

contains () n'appelle en fait que find () et vérifie la valeur null. Vous pouvez donc le réduire à:

if ($myModel = $model->collection->find($primaryId)) {
    var_dump($myModel);
}
5
Ziad Hilal

Je sais que cette question avait été posée avant la sortie de Laravel 5.0, mais à partir de Laravel 5.0, Collections prend en charge la méthode where() à cette fin.

Pour Laravel 5.0, 5.1 et 5.2, la méthode where() sur Collection ne fera qu'une comparaison égale. En outre, il effectue une comparaison stricte égale (===) par défaut. Pour effectuer une comparaison approximative (==), vous pouvez soit passer false en tant que troisième paramètre, soit utiliser la méthode whereLoose().

A partir de Laravel 5.3, la méthode where() a été développée pour fonctionner davantage comme la méthode where() pour le générateur de requêtes, qui accepte un opérateur comme deuxième paramètre. De même, comme pour le générateur de requêtes, l’opérateur utilisera par défaut une comparaison égale si aucune n’est fournie. La comparaison par défaut est également passée de strict par défaut à lâche par défaut. Donc, si vous souhaitez une comparaison stricte, vous pouvez utiliser whereStrict() ou simplement utiliser === en tant qu’opérateur de where().

Par conséquent, à partir de Laravel 5.0, le dernier exemple de code de la question fonctionnera exactement comme prévu:

$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');
5
patricus

Je dois souligner qu'il y a une erreur petite mais absolument CRITIQUE dans la réponse de Kalley. J'ai eu du mal avec cela pendant plusieurs heures avant de réaliser: 

Dans la fonction, ce que vous retournez est une comparaison, et quelque chose comme ceci serait plus correct: 

$desired_object = $food->filter(function($item) {
    return ($item->id **==** 24);
})->first();
3
squaretastic

Solution élégante pour trouver une valeur ( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value- pair-in-a-collection / ) peut être adapté:

$desired_object_key = $food->array_search(24, $food->lists('id'));
if ($desired_object_key !== false) {
   $desired_object = $food[$desired_object_key];
}
1
softfrog

A partir de Laravel 5.5, vous pouvez utiliser firstWhere ()

Dans votre cas:

$green_foods = $foods->firstWhere('color', 'green');
0
Victor Timoftii

Comme pour la question ci-dessus lorsque vous utilisez la clause where, vous devez également utiliser la méthode get Or first pour obtenir le résultat.

/**
*Get all food
*
*/

$foods = Food::all();

/**
*Get green food 
*
*/

$green_foods = Food::where('color', 'green')->get();
0
Marco