Saltar al contenido principal

Qué son los mensajes de orden superior

Los mensajes de orden superior (Higher-order Messages) son una sintaxis para invocar métodos de una colección accediendo a ellos como si fueran propiedades. Sin escribir una callback, puedes llamar a un método o acceder a una propiedad de cada elemento.
// Forma habitual
$names = $users->map(fn ($user) => $user->name);

// Forma con mensajes de orden superior
$names = $users->map->name;
$users->map->name significa «extrae la propiedad name de cada usuario y aplica map».

Cómo funciona — HigherOrderCollectionProxy

Cuando accedes a algo como $collection->map como si fuera una propiedad, se invoca __get() del trait EnumeratesValues.
// 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 el nombre está en $proxies, se devuelve una instancia de HigherOrderCollectionProxy. Este proxy conserva la colección y el nombre del método.

Proxy de acceso a propiedad mediante __get

Si a continuación accedes a una propiedad (->name), se invoca el __get() del proxy.
// 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};
    });
}
Es decir, $users->map->name equivale a:
$users->map(fn ($user) => $user->name);

Proxy de llamada a método mediante __call

Si en su lugar llamas a un método (->activate()), se invoca el __call() del proxy.
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() equivale a:
$users->each(fn ($user) => $user->activate());

Lista de métodos compatibles

Los métodos disponibles como mensajes de orden superior se declaran en el array $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',
];
Para añadir métodos personalizados, usa Collection::proxy().
Collection::proxy('myCustomMethod');

Casos de uso prácticos

Colecciones de modelos Eloquent

Los mensajes de orden superior son especialmente útiles con colecciones de Eloquent.
$users = User::with('posts')->get();

// Llamada a método: actualiza el estado de cada usuario
$users->each->markAsVerified();

// Acceso a propiedad: obtén solo los nombres
$names = $users->map->name;

// Con argumentos
$users->each->sendPasswordReset('es');

// Envío de notificación
$users->each->notify(new WelcomeNotification);

Filtrado

$activeUsers = $users->filter->isActive();

// Deja solo los usuarios para los que isActive() devuelve true
// Equivale a $users->filter(fn ($user) => $user->isActive())

$inactiveUsers = $users->reject->isActive();

Agregaciones

// Suma la propiedad post_count de cada usuario
$totalPosts = $users->sum->post_count;

// Máximo de la propiedad score
$highestScore = $users->max->score;

// Media de la propiedad age
$averageAge = $users->avg->age;

Agrupación y ordenación

// Agrupar por la propiedad role
$grouped = $users->groupBy->role;

// Ordenar ascendentemente por la propiedad name
$sorted = $users->sortBy->name;

// Ordenar descendentemente por created_at
$sorted = $users->sortByDesc->created_at;

Aplanar con flatMap

// Expande la relación posts de cada usuario en una única colección
$posts = $users->flatMap->posts;

Comprobaciones condicionales

// Comprueba si todos son administradores
$allAdmins = $users->every->isAdmin();

// Comprueba si al menos uno está activo
$hasActive = $users->contains->isActive();

// Comprueba si todos tienen un plan concreto
$allPro = $users->every->hasPlan('pro');

Colecciones de cadenas

También se pueden usar en colecciones de cadenas. El __call() del proxy trata las cadenas como si fueran una llamada estática.
$names = collect(['alice', 'bob', 'charlie']);

// Para colecciones de cadenas suele ser más claro usar una closure
$upper = $names->map(fn ($name) => strtoupper($name));
// ['ALICE', 'BOB', 'CHARLIE']
Para usar mensajes de orden superior con una colección de cadenas, el método debe existir estáticamente en el tipo string. Normalmente, encajan mejor con colecciones de modelos Eloquent o clases propias.

Comparación con closures normales

Los mensajes de orden superior son concisos, pero no encajan en todos los casos.
// Mensaje de orden superior: adecuado para obtener propiedades o llamar a métodos simples
$emails = $users->map->email;
$users->each->sendWelcomeMail();
$admins = $users->filter->isAdmin();

// Closure: adecuada para lógica compleja o cuando se necesitan argumentos
$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;
});
Cuando quieres «obtener la misma propiedad de cada elemento» o «llamar al mismo método en cada elemento», los mensajes de orden superior son más legibles. Si la condición es compleja o los argumentos son dinámicos, usa una closure.

Usar mensajes de orden superior en clases propias

Para aprovechar el mecanismo de HigherOrderCollectionProxy a través del trait EnumeratesValues, llama a Collection::proxy() para añadir el método al conjunto de proxies.
namespace App\Providers;

use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Añade una macro personalizada al conjunto de proxies
        Collection::macro('active', function () {
            return $this->filter(fn ($item) => $item->isActive());
        });

        Collection::proxy('active');
    }
}
// Tras el registro, ya se puede usar como mensaje de orden superior
$activeUsers = $users->active;

Próximos pasos

Trait Conditionable

Aprende la implementación interna de when() / unless() y cómo aplicarla en QueryBuilder y clases propias.
Última modificación el 13 de julio de 2026