Passer au contenu principal

Qu’est-ce qu’un message d’ordre supérieur

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 ».

Fonctionnement — HigherOrderCollectionProxy

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.php
public 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.

Proxy d’accès aux propriétés via __get

Si vous accédez ensuite à une propriété (->name), le __get() du proxy est appelé.
// src/Illuminate/Collections/HigherOrderCollectionProxy.php
public function __get($key)
{
    return $this->collection->{$this->method}(function ($value) use ($key) {
        return is_array($value) ? $value[$key] : $value->{$key};
    });
}
Autrement dit, $users->map->name équivaut à :
$users->map(fn ($user) => $user->name);

Proxy d’appel de méthode via __call

Si vous appelez ensuite une méthode (->activate()), le __call() du proxy est invoqué.
public function __call($method, $parameters)
{
    return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
        return is_string($value)
            ? $value::{$method}(...$parameters)
            : $value->{$method}(...$parameters);
    });
}
$users->each->activate() équivaut à :
$users->each(fn ($user) => $user->activate());

Liste des méthodes prises en charge

Les méthodes utilisables comme messages d’ordre supérieur sont définies dans le tableau $proxies.
protected static $proxies = [
    'average', 'avg',
    'contains', 'doesntContain', 'some',
    'each', 'every',
    'filter', 'reject',
    'first', 'last',
    'flatMap', 'map',
    'groupBy', 'keyBy',
    'hasMany', 'hasSole',
    'max', 'min', 'sum', 'percentage',
    'partition',
    'skipUntil', 'skipWhile',
    'sortBy', 'sortByDesc',
    'takeUntil', 'takeWhile',
    'unique',
    'unless', 'until', 'when',
];
Pour ajouter une méthode personnalisée, utilisez Collection::proxy().
Collection::proxy('myCustomMethod');

Cas d’utilisation pratiques

Collections de modèles Eloquent

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);

Filtrage

$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();

Agrégation

// 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 / tri

// 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;

Déployer une imbrication avec flatMap

// Déploie la relation posts de chaque utilisateur en une seule collection
$posts = $users->flatMap->posts;

Vérification de conditions

// 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');

Collections de chaînes

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.

Comparaison avec les closures classiques

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;

Étapes suivantes

Trait Conditionable

Découvrez l’implémentation interne de when() / unless() et son application à QueryBuilder ou à vos propres classes.
Dernière modification le 13 juillet 2026