Vai al contenuto principale

Cosa sono le Collection

Illuminate\Support\Collection è un wrapper fluido per array di dati. Invece di chiamare separatamente le funzioni array di PHP, con una catena di metodi trasformi i dati in modo intuitivo.
// Con funzioni array
$names = array_filter(
    array_map(fn ($user) => $user['name'], $users),
    fn ($name) => $name !== null
);

// Con Collection
$names = collect($users)
    ->pluck('name')
    ->filter()
    ->values();
Le Collection sono immutabili. Ogni metodo restituisce una nuova istanza senza modificare l’originale.

Creazione delle Collection

Helper collect()

use Illuminate\Support\Collection;

$users = collect([
    ['name' => 'Mario Rossi', 'age' => 28, 'role' => 'admin'],
    ['name' => 'Giulia Bianchi', 'age' => 34, 'role' => 'editor'],
    ['name' => 'Luca Verdi', 'age' => 22, 'role' => 'viewer'],
]);

Collection::make()

$collection = Collection::make([1, 2, 3]);

Collection::fromJson()

$collection = Collection::fromJson('[{"name":"Mario"},{"name":"Giulia"}]');

Metodi più usati

map — trasforma ogni elemento

$users = collect([
    ['name' => 'Mario Rossi', 'email' => '[email protected]'],
    ['name' => 'Giulia Bianchi', 'email' => '[email protected]'],
]);

$masked = $users->map(function (array $user) {
    $parts = explode('@', $user['email']);
    return [
        'name' => $user['name'],
        'email' => substr($parts[0], 0, 2) . '***@' . $parts[1],
    ];
});

filter / reject

$products = collect([
    ['name' => 'Laptop', 'price' => 1200, 'in_stock' => true],
    ['name' => 'Mouse', 'price' => 35, 'in_stock' => false],
    ['name' => 'Tastiera', 'price' => 80, 'in_stock' => true],
]);

// Solo disponibili
$inStock = $products->filter(fn ($product) => $product['in_stock']);

// Escludi i non disponibili
$available = $products->reject(fn ($product) => ! $product['in_stock']);

// filter() senza argomenti rimuove i valori falsy
$names = collect(['Mario', '', null, 'Giulia', false])->filter()->values();
// ['Mario', 'Giulia']
Dopo filter() gli indici possono avere “buchi”. Chiama values() per riottenere una sequenza da 0.

first / last

$orders = collect([
    ['id' => 1, 'status' => 'shipped', 'amount' => 50],
    ['id' => 2, 'status' => 'pending', 'amount' => 120],
    ['id' => 3, 'status' => 'pending', 'amount' => 35],
]);

$nextOrder = $orders->first(fn ($order) => $order['status'] === 'pending');
$order = $orders->first(fn ($order) => $order['status'] === 'cancelled', null);
$latest = $orders->last();

pluck

$users = collect([
    ['id' => 1, 'name' => 'Mario', 'department' => 'Sviluppo'],
    ['id' => 2, 'name' => 'Giulia', 'department' => 'Vendite'],
]);

$names = $users->pluck('name');           // ['Mario', 'Giulia']
$nameById = $users->pluck('name', 'id');  // [1 => 'Mario', 2 => 'Giulia']

groupBy

$byDepartment = $users->groupBy('department');

$byFirstChar = $users->groupBy(fn ($user) => mb_substr($user['name'], 0, 1));

sortBy / sortByDesc

$cheapFirst = $products->sortBy('price');
$expensiveFirst = $products->sortByDesc('price');

$sorted = $products->sortBy([
    ['price', 'asc'],
    ['name', 'asc'],
]);

each — esegui su ogni elemento

$orders->each(function (array $order) {
    \Log::info("Processing #{$order['id']}", ['amount' => $order['amount']]);
});

// Restituendo false esci dal ciclo
$orders->each(function (array $order) {
    if ($order['amount'] > 100) {
        return false;
    }
});

flatMap

$users = collect([
    ['name' => 'Mario', 'tags' => ['php', 'laravel']],
    ['name' => 'Giulia', 'tags' => ['javascript', 'vue']],
]);

$allTags = $users->flatMap(fn ($user) => $user['tags']);
// ['php', 'laravel', 'javascript', 'vue']

reduce — aggrega

$total = $orders->reduce(
    fn ($carry, $order) => $carry + ($order['price'] * $order['quantity']),
    0
);
Per somme semplici puoi usare sum().

reduceInto — aggrega su oggetto

Simile a reduce, ma la callback non deve restituire nulla. Utile per modificare direttamente un oggetto.
class OrderStats
{
    public int $total = 0;
    public int $count = 0;
}

$stats = $orders->reduceInto(new OrderStats, function (OrderStats $stats, array $order) {
    $stats->total += $order['amount'];
    $stats->count++;
});
Per accumulare su array/scalari usa il passaggio per riferimento.
$even = $collection->reduceInto([], function (array &$result, int $value) {
    if ($value % 2 === 0) {
        $result[] = $value;
    }
});

chunk — dividi in batch

$users = collect(range(1, 100))->map(fn ($i) => ['id' => $i, 'name' => "Utente {$i}"]);

$chunks = $users->chunk(10);

$chunks->each(function (\Illuminate\Support\Collection $batch) {
    // Elabora il batch
});

Concatenazione dei metodi

$result = $orders
    ->filter(fn ($order) => $order['status'] === 'completed')
    ->filter(fn ($order) => $order['category'] === 'elettronica')
    ->sortByDesc('amount')
    ->map(fn ($order) => [
        'customer' => $order['customer'],
        'amount'   => number_format($order['amount']) . ' EUR',
    ])
    ->values();

Integrazione con Eloquent

I risultati delle query Eloquent sono sempre istanze di Illuminate\Database\Eloquent\Collection, che estende Collection.
use App\Models\User;
use App\Models\Order;

$users = User::where('is_active', true)->get(); // Collection

$adminEmails = User::all()
    ->filter(fn ($user) => $user->role === 'admin')
    ->pluck('email');

$orders = Order::with('items')->where('status', 'completed')->get();

$summary = $orders->map(fn ($order) => [
    'id'         => $order->id,
    'customer'   => $order->user->name,
    'item_count' => $order->items->count(),
    'total'      => $order->items->sum('price'),
]);
Fai filtri e ordinamenti dove possono essere fatti dal DB (con query builder), non con le Collection: altrimenti carichi in memoria dati inutili.

Metodi specifici di Eloquent\Collection

$users = User::all();

$user = $users->find(1);
$ids = $users->modelKeys();
$users->load('orders', 'profile');
$diff = $users->diff($otherUsers);
$intersect = $users->intersect($otherUsers);

Lazy Collection

Le Collection normali caricano tutti i dati in memoria. LazyCollection usa i generator PHP per processare un elemento alla volta. Utile con decine di migliaia di record.
use Illuminate\Support\LazyCollection;

// In memoria tutto
$users = User::all();

// Uno alla volta
User::cursor()->each(function (User $user) {
    // In memoria un utente per volta
});

Creare una LazyCollection

use Illuminate\Support\LazyCollection;

$lazy = LazyCollection::make(function () {
    $handle = fopen('large-file.csv', 'r');

    while (($line = fgetcsv($handle)) !== false) {
        yield $line;
    }

    fclose($handle);
});

$lazy = User::where('is_active', true)->cursor();

Batch di grandi volumi

use App\Models\Order;

Order::cursor()
    ->filter(fn ($order) => $order->amount > 100)
    ->each(fn ($order) => $order->sendConfirmationEmail());

Order::where('status', 'completed')
    ->cursor()
    ->filter(fn ($order) => $order->amount > 100)
    ->each(fn ($order) => $order->sendConfirmationEmail());
cursor() recupera un record alla volta dal DB, risparmiando memoria; la connessione al DB resta aperta fino alla fine.

Paginazione con take e skip

$lazy = LazyCollection::make(function () {
    foreach (range(1, 1000000) as $i) {
        yield $i;
    }
});

$page = $lazy->skip(1000)->take(100)->values();
MetodoDescrizione
collect($array)Crea una Collection
map($callback)Trasforma ogni elemento
filter($callback)Filtra
reject($callback)Esclude
first($callback)Primo che soddisfa la condizione
pluck($key)Estrai una chiave
groupBy($key)Raggruppa
sortBy($key)Ordina crescente
sortByDesc($key)Ordina decrescente
each($callback)Esegui per ogni elemento
flatMap($callback)Mappa e appiattisci
reduce($callback, $initial)Aggrega in un valore
chunk($size)Suddividi
values()Rinumera indici
sum($key)Somma
count()Conta
Le Collection migliorano molto la leggibilità.
// Con funzioni array
$result = array_values(array_filter(
    array_map(fn ($u) => $u['name'], $users),
    fn ($name) => strlen($name) > 2
));

// Con Collection
$result = collect($users)
    ->pluck('name')
    ->filter(fn ($name) => strlen($name) > 2)
    ->values()
    ->all();
  • Collection: centinaia/migliaia di record, semplice.
  • LazyCollection: decine di migliaia, memoria contenuta; combinala con cursor().
  • get() restituisce una Collection, cursor() una LazyCollection.
Ultima modifica il 13 luglio 2026