Qu’est-ce qu’une Collection
Illuminate\Support\Collection est un wrapper fluide pour manipuler des tableaux.
Plutôt que d’enchaîner les fonctions natives, vous chaînez intuitivement les traitements.
// Approche « tableau »
$names = array_filter (
array_map ( fn ( $user ) => $user [ 'name' ], $users ),
fn ( $name ) => $name !== null
);
// Approche Collection
$names = collect ( $users )
-> pluck ( 'name' )
-> filter ()
-> values ();
Les collections sont immuables : chaque méthode retourne une nouvelle instance sans modifier l’originale. Vous manipulez en toute sécurité.
Créer une Collection
Helper collect()
Le plus courant.
use Illuminate\Support\ Collection ;
$users = collect ([
[ 'name' => 'Taro Yamada' , 'age' => 28 , 'role' => 'admin' ],
[ 'name' => 'Hanako Suzuki' , 'age' => 34 , 'role' => 'editor' ],
[ 'name' => 'Jiro Sato' , 'age' => 22 , 'role' => 'viewer' ],
]);
$products = collect ([
[ 'name' => 'PC portable' , 'price' => 120000 , 'stock' => 5 ],
[ 'name' => 'Souris' , 'price' => 3500 , 'stock' => 20 ],
[ 'name' => 'Clavier' , 'price' => 8000 , 'stock' => 12 ],
]);
Collection::make()
Équivalent, style façade.
$collection = Collection :: make ([ 1 , 2 , 3 ]);
Collection::fromJson()
Depuis une chaîne JSON — pratique pour les réponses d’API.
$collection = Collection :: fromJson ( '[{"name":"Taro Yamada"},{"name":"Hanako Suzuki"}]' );
Méthodes courantes
$users = collect ([
[ 'name' => 'Taro Yamada' , 'email' => '[email protected] ' ],
[ 'name' => 'Hanako Suzuki' , 'email' => '[email protected] ' ],
]);
// Masquer l'e-mail
$masked = $users -> map ( function ( array $user ) {
$parts = explode ( '@' , $user [ 'email' ]);
return [
'name' => $user [ 'name' ],
'email' => substr ( $parts [ 0 ], 0 , 2 ) . '***@' . $parts [ 1 ],
];
});
filter / reject — Filtrer
filter conserve ce qui satisfait la condition, reject fait l’inverse.
$products = collect ([
[ 'name' => 'PC portable' , 'price' => 120000 , 'in_stock' => true ],
[ 'name' => 'Souris' , 'price' => 3500 , 'in_stock' => false ],
[ 'name' => 'Clavier' , 'price' => 8000 , 'in_stock' => true ],
]);
$inStock = $products -> filter ( fn ( $product ) => $product [ 'in_stock' ]);
$available = $products -> reject ( fn ( $product ) => ! $product [ 'in_stock' ]);
// Sans argument, retire les valeurs falsy
$names = collect ([ 'Yamada' , '' , null , 'Suzuki' , false ]) -> filter () -> values ();
// ['Yamada', 'Suzuki']
Après filter, les indices deviennent trous. values() renumérote à partir de 0.
first / last
$orders = collect ([
[ 'id' => 1 , 'status' => 'shipped' , 'amount' => 5000 ],
[ 'id' => 2 , 'status' => 'pending' , 'amount' => 12000 ],
[ 'id' => 3 , 'status' => 'pending' , 'amount' => 3500 ],
]);
$nextOrder = $orders -> first ( fn ( $order ) => $order [ 'status' ] === 'pending' );
$order = $orders -> first ( fn ( $order ) => $order [ 'status' ] === 'cancelled' , null );
$latest = $orders -> last ();
$users = collect ([
[ 'id' => 1 , 'name' => 'Taro Yamada' , 'department' => 'Dev' ],
[ 'id' => 2 , 'name' => 'Hanako Suzuki' , 'department' => 'Sales' ],
[ 'id' => 3 , 'name' => 'Jiro Sato' , 'department' => 'Dev' ],
]);
$names = $users -> pluck ( 'name' );
$nameById = $users -> pluck ( 'name' , 'id' );
groupBy
$users = collect ([
[ 'name' => 'Taro Yamada' , 'department' => 'Dev' ],
[ 'name' => 'Hanako Suzuki' , 'department' => 'Sales' ],
[ 'name' => 'Jiro Sato' , 'department' => 'Dev' ],
[ 'name' => 'Misaki Tanaka' , 'department' => 'Sales' ],
]);
$byDepartment = $users -> groupBy ( 'department' );
// Clé dynamique via closure
$byFirstChar = $users -> groupBy ( fn ( $user ) => mb_substr ( $user [ 'name' ], 0 , 1 ));
sortBy / sortByDesc
$products = collect ([
[ 'name' => 'PC portable' , 'price' => 120000 ],
[ 'name' => 'Souris' , 'price' => 3500 ],
[ 'name' => 'Clavier' , 'price' => 8000 ],
]);
$cheapFirst = $products -> sortBy ( 'price' );
$expensiveFirst = $products -> sortByDesc ( 'price' );
// Plusieurs clés
$sorted = $products -> sortBy ([
[ 'price' , 'asc' ],
[ 'name' , 'asc' ],
]);
each — Effets de bord
Pour la journalisation, l’envoi de mails, etc. La collection n’est pas modifiée.
$orders = collect ([
[ 'id' => 1 , 'user_id' => 10 , 'amount' => 5000 ],
[ 'id' => 2 , 'user_id' => 11 , 'amount' => 12000 ],
]);
$orders -> each ( function ( array $order ) {
\ Log :: info ( "Traitement de la commande #{ $order ['id']}" , [ 'amount' => $order [ 'amount' ]]);
});
// Retourner false interrompt
$orders -> each ( function ( array $order ) {
if ( $order [ 'amount' ] > 10000 ) {
return false ;
}
});
flatMap
Transforme chaque élément en tableau puis aplatit d’un niveau.
$users = collect ([
[ 'name' => 'Taro Yamada' , 'tags' => [ 'php' , 'laravel' ]],
[ 'name' => 'Hanako Suzuki' , 'tags' => [ 'javascript' , 'vue' ]],
]);
$allTags = $users -> flatMap ( fn ( $user ) => $user [ 'tags' ]);
// ['php', 'laravel', 'javascript', 'vue']
reduce
Réduit la collection à une seule valeur.
$orders = collect ([
[ 'product' => 'PC portable' , 'quantity' => 1 , 'price' => 120000 ],
[ 'product' => 'Souris' , 'quantity' => 2 , 'price' => 3500 ],
[ 'product' => 'Clavier' , 'quantity' => 1 , 'price' => 8000 ],
]);
$total = $orders -> reduce (
fn ( $carry , $order ) => $carry + ( $order [ 'price' ] * $order [ 'quantity' ]),
0
);
// 135000
Pour une simple somme, utilisez sum() : $orders->sum(fn ($o) => $o['price'] * $o['quantity']).
reduceInto
Comme reduce, mais la callback n’a pas besoin de retourner de valeur. Pratique pour muter un objet directement.
class OrderStats
{
public int $total = 0 ;
public int $count = 0 ;
}
$orders = collect ([
[ 'amount' => 100 ],
[ 'amount' => 250 ],
[ 'amount' => 50 ],
]);
$stats = $orders -> reduceInto ( new OrderStats , function ( OrderStats $stats , array $order ) {
$stats -> total += $order [ 'amount' ];
$stats -> count ++ ;
});
$stats -> total ; // 400
$stats -> count ; // 3
reduce pour les valeurs primitives ; reduceInto pour muter un objet.
Pour des scalaires ou tableaux, utilisez le passage par référence (&).
$collection = collect ([ 1 , 2 , 3 , 4 , 5 ]);
$even = $collection -> reduceInto ([], function ( array & $result , int $value ) {
if ( $value % 2 === 0 ) {
$result [] = $value ;
}
});
// [2, 4]
chunk
Découpe en lots de taille fixe.
$users = collect ( range ( 1 , 100 )) -> map ( fn ( $i ) => [ 'id' => $i , 'name' => "User { $i }" ]);
$chunks = $users -> chunk ( 10 );
$chunks -> each ( function ( \Illuminate\Support\ Collection $batch ) {
// Traitement par lot
});
Chaînage
La force des collections : chaîner les opérations en une seule expression.
$orders = collect ([
[ 'customer' => 'Taro Yamada' , 'status' => 'completed' , 'amount' => 5000 , 'category' => 'Électronique' ],
[ 'customer' => 'Hanako Suzuki' , 'status' => 'pending' , 'amount' => 12000 , 'category' => 'Électronique' ],
[ 'customer' => 'Jiro Sato' , 'status' => 'completed' , 'amount' => 3500 , 'category' => 'Papeterie' ],
[ 'customer' => 'Misaki Tanaka' , 'status' => 'completed' , 'amount' => 8000 , 'category' => 'Électronique' ],
[ 'customer' => 'Kenichi Ito' , 'status' => 'cancelled' , 'amount' => 2000 , 'category' => 'Papeterie' ],
]);
$result = $orders
-> filter ( fn ( $order ) => $order [ 'status' ] === 'completed' )
-> filter ( fn ( $order ) => $order [ 'category' ] === 'Électronique' )
-> sortByDesc ( 'amount' )
-> map ( fn ( $order ) => [
'customer' => $order [ 'customer' ],
'amount' => number_format ( $order [ 'amount' ]) . ' ¥' ,
])
-> values ();
Intégration avec Eloquent
Les résultats Eloquent sont des Illuminate\Database\Eloquent\Collection, qui héritent des collections de base.
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' ),
]);
Effectuez filtrage et tri en SQL (where, orderBy) quand c’est possible. Filtrer une collection après coup charge inutilement toutes les données en mémoire.
Méthodes propres à Eloquent
$users = User :: all ();
$user = $users -> find ( 1 );
$ids = $users -> modelKeys ();
$users -> load ( 'orders' , 'profile' );
$diff = $users -> diff ( $otherUsers );
$intersect = $users -> intersect ( $otherUsers );
Lazy Collections
Les collections classiques chargent tout en mémoire. LazyCollection utilise les générateurs PHP pour traiter élément par élément — précieux sur de gros volumes.
use Illuminate\Support\ LazyCollection ;
// Classique : tout en mémoire
$users = User :: all (); // 100 000 → tout en RAM
// Lazy : un élément à la fois
User :: cursor () -> each ( function ( User $user ) {
// Traitement individuel
});
Créer une 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 ();
Traitement de masse
use App\Models\ Order ;
Order :: cursor ()
-> filter ( fn ( $order ) => $order -> amount > 10000 )
-> each ( fn ( $order ) => $order -> sendConfirmationEmail ());
Order :: where ( 'status' , 'completed' )
-> cursor ()
-> filter ( fn ( $order ) => $order -> amount > 10000 )
-> each ( fn ( $order ) => $order -> sendConfirmationEmail ());
cursor() récupère les lignes une par une, économisant la mémoire. La connexion DB reste ouverte jusqu’à la fin.
$lazy = LazyCollection :: make ( function () {
foreach ( range ( 1 , 1000000 ) as $i ) {
yield $i ;
}
});
$page = $lazy -> skip ( 1000 ) -> take ( 100 ) -> values ();
Récapitulatif
Méthode Description collect($array)Créer une Collection map($callback)Transformer chaque élément filter($callback)Filtrer reject($callback)Filtrer par exclusion first($callback)Premier correspondant pluck($key)Extraire un champ groupBy($key)Regrouper sortBy($key)Tri ascendant sortByDesc($key)Tri descendant each($callback)Effet de bord flatMap($callback)Map + aplatir reduce($callback, $initial)Réduction chunk($size)Découper en lots values()Renuméroter sum($key)Somme count()Nombre d’éléments
Collection vs fonctions natives
Les collections sont bien plus lisibles. // Natif
$result = array_values ( array_filter (
array_map ( fn ( $u ) => $u [ 'name' ], $users ),
fn ( $name ) => strlen ( $name ) > 2
));
// Collection
$result = collect ( $users )
-> pluck ( 'name' )
-> filter ( fn ( $name ) => strlen ( $name ) > 2 )
-> values ()
-> all ();
Collection vs LazyCollection
Collection : quelques centaines à quelques milliers d’éléments. Simple et intuitif.
LazyCollection : dizaines de milliers d’éléments et plus. Économe en mémoire. Idéal avec cursor().
get() retourne une Collection, cursor() retourne une LazyCollection.