Les messages d’ordre supérieur (Higher-order Messages) sont une syntaxe qui permet d’appeler les méthodes d’une collection sous la forme d’un accès à une propriété. Sans écrire de callback, vous pouvez appeler une méthode ou récupérer une propriété sur chaque élément.
// Écriture classique$names = $users->map(fn ($user) => $user->name);// Écriture avec un message d'ordre supérieur$names = $users->map->name;
$users->map->name signifie « extraire la propriété name de chaque utilisateur et faire un map ».
Lorsque vous accédez à $collection->map comme à une propriété, la méthode __get() du trait EnumeratesValues est appelée.
// src/Illuminate/Collections/Traits/EnumeratesValues.phppublic function __get($key){ if (! in_array($key, static::$proxies)) { throw new Exception("Property [{$key}] does not exist on this collection instance."); } return new HigherOrderCollectionProxy($this, $key);}
Si le nom de la méthode figure dans $proxies, une instance de HigherOrderCollectionProxy est retournée. Ce proxy conserve deux informations : la collection et le nom de la méthode.
Les messages d’ordre supérieur sont particulièrement pratiques avec les collections Eloquent.
$users = User::with('posts')->get();// Appel de méthode : mettre à jour le statut de chaque utilisateur$users->each->markAsVerified();// Accès à une propriété : ne récupérer que les noms$names = $users->map->name;// Passer un argument à la méthode$users->each->sendPasswordReset('ja');// Envoi d'e-mail$users->each->notify(new WelcomeNotification);
$activeUsers = $users->filter->isActive();// Ne garde que les utilisateurs pour lesquels isActive() renvoie true// Équivalent à $users->filter(fn ($user) => $user->isActive())$inactiveUsers = $users->reject->isActive();
// Somme des propriétés post_count de chaque utilisateur$totalPosts = $users->sum->post_count;// Score maximum parmi les propriétés score$highestScore = $users->max->score;// Moyenne des âges$averageAge = $users->avg->age;
// Groupement par propriété role$grouped = $users->groupBy->role;// Tri croissant par propriété name$sorted = $users->sortBy->name;// Tri décroissant par propriété created_at$sorted = $users->sortByDesc->created_at;
// Vérifier si tout le monde est administrateur$allAdmins = $users->every->isAdmin();// Vérifier si au moins un utilisateur est actif$hasActive = $users->contains->isActive();// Vérifier si tout le monde a un plan spécifique$allPro = $users->every->hasPlan('pro');
Cela fonctionne aussi pour des collections dont les valeurs sont des chaînes. Le __call() du proxy les appelle alors comme méthodes statiques.
$names = collect(['alice', 'bob', 'charlie']);// Pour une collection de chaînes, une closure reste plus explicite$upper = $names->map(fn ($name) => strtoupper($name));// ['ALICE', 'BOB', 'CHARLIE']
Utiliser des messages d’ordre supérieur sur une collection de chaînes nécessite que la méthode soit définie statiquement sur le type string. En pratique, c’est plus naturel avec des collections de modèles Eloquent ou de classes maison.
Les messages d’ordre supérieur sont concis, mais ne conviennent pas à tous les cas.
// Messages d'ordre supérieur : parfaits pour un accès simple à une propriété ou un appel de méthode$emails = $users->map->email;$users->each->sendWelcomeMail();$admins = $users->filter->isAdmin();// Closures : préférables lorsque la logique est complexe ou qu'un argument est nécessaire$formatted = $users->map(function ($user) { return "{$user->name} <{$user->email}>";});$filtered = $users->filter(function ($user) use ($minAge, $role) { return $user->age >= $minAge && $user->role === $role;});
Pour « extraire la même propriété de chaque élément » ou « appeler la même méthode sur chaque élément », les messages d’ordre supérieur sont plus lisibles. Si la condition est complexe ou si les arguments sont dynamiques, préférez les closures.
Utiliser des messages d’ordre supérieur avec vos classes personnalisées
Pour bénéficier du mécanisme HigherOrderCollectionProxy via le trait EnumeratesValues, appelez Collection::proxy() pour ajouter la méthode aux cibles du proxy.
namespace App\Providers;use Illuminate\Support\Collection;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{ public function boot(): void { // Ajoute une macro personnalisée aux cibles du proxy Collection::macro('active', function () { return $this->filter(fn ($item) => $item->isActive()); }); Collection::proxy('active'); }}
// Après enregistrement, utilisable comme message d'ordre supérieur$activeUsers = $users->active;