Vai al contenuto principale

Cosa sono gli higher-order messages

Gli higher-order messages sono una sintassi che permette di chiamare i metodi di una collection nella forma di un accesso a proprietà. Senza scrivere callback, puoi invocare un metodo su ogni elemento o ottenerne una proprietà.
// scrittura tradizionale
$names = $users->map(fn ($user) => $user->name);

// scrittura con higher-order messages
$names = $users->map->name;
$users->map->name significa “estrai la proprietà name di ogni utente e applica map”.

Meccanismo — HigherOrderCollectionProxy

Accedendo come proprietà con $collection->map, viene invocato __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);
}
Se il nome del metodo è incluso in $proxies, viene restituita un’istanza di HigherOrderCollectionProxy. Questo proxy conserva due elementi: la collection e il nome del metodo.

Proxy di accesso a proprietà con __get

Accedendo poi a una proprietà (->name), viene invocato __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};
    });
}
In pratica $users->map->name è equivalente a:
$users->map(fn ($user) => $user->name);

Proxy di chiamata a metodo con __call

Chiamando poi un metodo (->activate()), viene invocato __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() è equivalente a:
$users->each(fn ($user) => $user->activate());

Elenco dei metodi supportati

I metodi utilizzabili come higher-order messages sono definiti nell’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',
];
Per aggiungere metodi personalizzati usa Collection::proxy().
Collection::proxy('myCustomMethod');

Casi d’uso pratici

Collection di modelli Eloquent

Gli higher-order messages sono particolarmente comodi con le collection Eloquent.
$users = User::with('posts')->get();

// chiamata a metodo: aggiorna lo stato di ogni utente
$users->each->markAsVerified();

// accesso a proprietà: estrai solo i nomi
$names = $users->map->name;

// passa argomenti al metodo
$users->each->sendPasswordReset('it');

// invio email
$users->each->notify(new WelcomeNotification);

Filtraggio

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

// mantiene solo gli utenti per cui isActive() restituisce true
// equivalente a $users->filter(fn ($user) => $user->isActive())

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

Aggregazioni

// somma sulla proprietà post_count di ogni utente
$totalPosts = $users->sum->post_count;

// massimo sullo score di ogni utente
$highestScore = $users->max->score;

// media sull'age di ogni utente
$averageAge = $users->avg->age;

Raggruppamento e ordinamento

// raggruppa per proprietà role
$grouped = $users->groupBy->role;

// ordinamento crescente per proprietà name
$sorted = $users->sortBy->name;

// ordinamento decrescente per proprietà created_at
$sorted = $users->sortByDesc->created_at;

Appiattire l’annidamento con flatMap

// espande le relazioni posts di ogni utente in un'unica collection
$posts = $users->flatMap->posts;

Verifiche condizionali

// verifica che tutti siano admin
$allAdmins = $users->every->isAdmin();

// verifica che almeno uno sia attivo
$hasActive = $users->contains->isActive();

// verifica che tutti abbiano un piano specifico
$allPro = $users->every->hasPlan('pro');

Collection di stringhe

Funziona anche con collection di stringhe. __call() del proxy, in caso di stringa, invoca il metodo come statico.
$names = collect(['alice', 'bob', 'charlie']);

// per collection di stringhe la closure è più esplicita
$upper = $names->map(fn ($name) => strtoupper($name));
// ['ALICE', 'BOB', 'CHARLIE']
Perché gli higher-order messages funzionino con collection di stringhe, il metodo deve essere definito staticamente sul tipo stringa. Di solito sono più naturali con collection di modelli Eloquent o classi personalizzate.

Confronto con le normali closure

Gli higher-order messages sono concisi, ma non applicabili in ogni caso.
// higher-order messages: ideali per semplici accessi a proprietà o chiamate a metodo
$emails = $users->map->email;
$users->each->sendWelcomeMail();
$admins = $users->filter->isAdmin();

// closure: adatte a logica complessa o quando servono argomenti
$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;
});
Se stai “estraendo la stessa proprietà da ogni elemento” o “chiamando lo stesso metodo su ogni elemento”, gli higher-order messages sono più leggibili. Se la condizione è complessa o gli argomenti sono dinamici, usa una closure.

Usare gli higher-order messages in classi personalizzate

Per sfruttare il meccanismo di HigherOrderCollectionProxy tramite il trait EnumeratesValues, chiami Collection::proxy() per aggiungere il metodo tra quelli proxificabili.
namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // aggiungi una macro personalizzata tra i target proxy
        Collection::macro('active', function () {
            return $this->filter(fn ($item) => $item->isActive());
        });

        Collection::proxy('active');
    }
}
// dopo la registrazione è utilizzabile come higher-order message
$activeUsers = $users->active;

Prossimi passi

Trait Conditionable

Impara l’implementazione interna di when() / unless() e come applicarlo al QueryBuilder e alle tue classi.
Ultima modifica il 13 luglio 2026