> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Collections

> Manipulez efficacement des données avec la classe Collection de Laravel.

## 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.

```php theme={null}
// 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();
```

<Info>
  Les collections sont **immuables** : chaque méthode retourne une nouvelle instance sans modifier l'originale. Vous manipulez en toute sécurité.
</Info>

## Créer une Collection

### Helper `collect()`

Le plus courant.

```php theme={null}
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.

```php theme={null}
$collection = Collection::make([1, 2, 3]);
```

### `Collection::fromJson()`

Depuis une chaîne JSON — pratique pour les réponses d'API.

```php theme={null}
$collection = Collection::fromJson('[{"name":"Taro Yamada"},{"name":"Hanako Suzuki"}]');
```

## Méthodes courantes

### `map` — Transformer chaque élément

```php theme={null}
$users = collect([
    ['name' => 'Taro Yamada', 'email' => 'yamada@example.com'],
    ['name' => 'Hanako Suzuki', 'email' => 'suzuki@example.com'],
]);

// 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.

```php theme={null}
$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']
```

<Tip>
  Après `filter`, les indices deviennent trous. `values()` renumérote à partir de 0.
</Tip>

### `first` / `last`

```php theme={null}
$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();
```

### `pluck` — Extraire un champ

```php theme={null}
$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`

```php theme={null}
$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`

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
$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
```

<Tip>
  Pour une simple somme, utilisez `sum()` : `$orders->sum(fn ($o) => $o['price'] * $o['quantity'])`.
</Tip>

### `reduceInto`

Comme `reduce`, mais la callback n'a pas besoin de retourner de valeur. Pratique pour muter un objet directement.

```php theme={null}
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
```

<Tip>
  `reduce` pour les valeurs primitives ; `reduceInto` pour muter un objet.
</Tip>

Pour des scalaires ou tableaux, utilisez le passage par référence (`&`).

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
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'),
]);
```

<Warning>
  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.
</Warning>

### Méthodes propres à Eloquent

```php theme={null}
$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.

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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());
```

<Info>
  `cursor()` récupère les lignes une par une, économisant la mémoire. La connexion DB reste ouverte jusqu'à la fin.
</Info>

### Pagination avec `take` / `skip`

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

$page = $lazy->skip(1000)->take(100)->values();
```

## Récapitulatif

<AccordionGroup>
  <Accordion title="Méthodes fréquentes">
    | 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          |
  </Accordion>

  <Accordion title="Collection vs fonctions natives">
    Les collections sont bien plus lisibles.

    ```php theme={null}
    // 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();
    ```
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>


## Related topics

- [Collections Eloquent](/fr/eloquent-collections.md)
- [Collection Deep Dive](/fr/advanced/collection-deep-dive.md)
- [Messages d'ordre supérieur des collections](/fr/advanced/higher-order-messages.md)
- [laravel/agent-skills — la collection officielle de skills d'agents IA Laravel](/fr/blog/agent-skills-introduction.md)
- [Eloquent API Resources](/fr/eloquent-resources.md)
