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à.
Accedendo come proprietà con $collection->map, viene invocato __get() del trait EnumeratesValues.
// 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);}
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.
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);
$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();
// 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;
// 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;
// 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');
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.
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;