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

> Erläutert, wie Sie mit der Collection-Klasse von Laravel Daten effizient bearbeiten.

## Was sind Collections?

Die Klasse `Illuminate\Support\Collection` ist ein fluenter Wrapper für Array-Daten.
Statt einzelne PHP-Standardfunktionen für Arrays aneinanderzureihen, können Sie Daten mit Methoden-Chaining intuitiv verarbeiten.

```php theme={null}
// Verarbeitung mit gewöhnlichen Array-Funktionen
$names = array_filter(
    array_map(fn ($user) => $user['name'], $users),
    fn ($name) => $name !== null
);

// Mit Collections
$names = collect($users)
    ->pluck('name')
    ->filter()
    ->values();
```

<Info>
  Collections sind **immutable** (unveränderlich). Jede Methode verändert die ursprüngliche Collection nicht, sondern liefert eine neue Instanz zurück. Sie können sicher arbeiten, ohne die Originaldaten zu verlieren.
</Info>

## Collections erzeugen

### Helper `collect()`

Die gebräuchlichste Variante. Übergeben Sie ein Array, um eine Collection zu erzeugen.

```php theme={null}
use Illuminate\Support\Collection;

// Collection aus einem Array erzeugen
$users = collect([
    ['name' => 'Yamada Taro', 'age' => 28, 'role' => 'admin'],
    ['name' => 'Suzuki Hanako', 'age' => 34, 'role' => 'editor'],
    ['name' => 'Sato Jiro', 'age' => 22, 'role' => 'viewer'],
]);

// Auch verschachtelte Arrays sind möglich
$products = collect([
    ['name' => 'Notebook', 'price' => 120000, 'stock' => 5],
    ['name' => 'Maus', 'price' => 3500, 'stock' => 20],
    ['name' => 'Tastatur', 'price' => 8000, 'stock' => 12],
]);
```

### `Collection::make()`

Entspricht `collect()`. Verwenden Sie diese Form, wenn Sie den Facade-Stil bevorzugen.

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

### `Collection::fromJson()`

Erzeugt eine Collection aus einem JSON-String. Praktisch für die Verarbeitung externer API-Antworten.

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

## Häufig genutzte Methoden

### `map` – jedes Element transformieren

Wendet eine Funktion auf jedes Element an und liefert eine neue Collection mit den transformierten Werten zurück.

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

// E-Mail-Adressen für die Anzeige maskieren
$masked = $users->map(function (array $user) {
    $parts = explode('@', $user['email']);
    return [
        'name' => $user['name'],
        'email' => substr($parts[0], 0, 2) . '***@' . $parts[1],
    ];
});

// [['name' => 'Yamada Taro', 'email' => 'ya***@example.com'], ...]
```

### `filter` / `reject` – nach Bedingung filtern

`filter()` behält nur die Elemente, die der Bedingung entsprechen; `reject()` schließt diese aus.

```php theme={null}
$products = collect([
    ['name' => 'Notebook', 'price' => 120000, 'in_stock' => true],
    ['name' => 'Maus', 'price' => 3500, 'in_stock' => false],
    ['name' => 'Tastatur', 'price' => 8000, 'in_stock' => true],
]);

// Nur Produkte mit Lagerbestand
$inStock = $products->filter(fn ($product) => $product['in_stock']);

// Produkte ohne Lagerbestand ausschließen (reject ist die Umkehrung von filter)
$available = $products->reject(fn ($product) => ! $product['in_stock']);

// Ohne Argument entfernt filter() alle falsy-Werte
$names = collect(['Yamada', '', null, 'Suzuki', false])->filter()->values();
// ['Yamada', 'Suzuki']
```

<Tip>
  Nach `filter()` können Indizes lückenhaft werden. Ein Aufruf von `values()` weist die Indizes ab 0 neu zu.
</Tip>

### `first` / `last` – einzelnes Element ermitteln

Liefert das erste bzw. letzte Element, das die Bedingung erfüllt.

```php theme={null}
$orders = collect([
    ['id' => 1, 'status' => 'shipped', 'amount' => 5000],
    ['id' => 2, 'status' => 'pending', 'amount' => 12000],
    ['id' => 3, 'status' => 'pending', 'amount' => 3500],
]);

// Die nächste offene Bestellung ermitteln
$nextOrder = $orders->first(fn ($order) => $order['status'] === 'pending');
// ['id' => 2, 'status' => 'pending', 'amount' => 12000]

// Standardwert, falls keine Bedingung zutrifft
$order = $orders->first(fn ($order) => $order['status'] === 'cancelled', null);

// Letztes Element
$latest = $orders->last();
```

### `pluck` – nur einen bestimmten Schlüssel extrahieren

Extrahiert aus verschachtelten Arrays oder Modellen ein bestimmtes Feld.

```php theme={null}
$users = collect([
    ['id' => 1, 'name' => 'Yamada Taro', 'department' => 'Entwicklung'],
    ['id' => 2, 'name' => 'Suzuki Hanako', 'department' => 'Vertrieb'],
    ['id' => 3, 'name' => 'Sato Jiro', 'department' => 'Entwicklung'],
]);

// Nur die Namen extrahieren
$names = $users->pluck('name');
// ['Yamada Taro', 'Suzuki Hanako', 'Sato Jiro']

// Mapping mit id als Schlüssel
$nameById = $users->pluck('name', 'id');
// [1 => 'Yamada Taro', 2 => 'Suzuki Hanako', 3 => 'Sato Jiro']
```

### `groupBy` – nach einem Schlüssel gruppieren

```php theme={null}
$users = collect([
    ['name' => 'Yamada Taro', 'department' => 'Entwicklung'],
    ['name' => 'Suzuki Hanako', 'department' => 'Vertrieb'],
    ['name' => 'Sato Jiro', 'department' => 'Entwicklung'],
    ['name' => 'Tanaka Misaki', 'department' => 'Vertrieb'],
]);

$byDepartment = $users->groupBy('department');
// [
//   'Entwicklung' => [['name' => 'Yamada Taro', ...], ['name' => 'Sato Jiro', ...]],
//   'Vertrieb'    => [['name' => 'Suzuki Hanako', ...], ['name' => 'Tanaka Misaki', ...]],
// ]

// Gruppenschlüssel dynamisch per Closure bestimmen
$byFirstChar = $users->groupBy(fn ($user) => mb_substr($user['name'], 0, 1));
```

### `sortBy` / `sortByDesc` – sortieren

```php theme={null}
$products = collect([
    ['name' => 'Notebook', 'price' => 120000],
    ['name' => 'Maus', 'price' => 3500],
    ['name' => 'Tastatur', 'price' => 8000],
]);

// Preis aufsteigend
$cheapFirst = $products->sortBy('price');

// Preis absteigend
$expensiveFirst = $products->sortByDesc('price');

// Nach mehreren Schlüsseln sortieren
$sorted = $products->sortBy([
    ['price', 'asc'],
    ['name', 'asc'],
]);
```

### `each` – Verarbeitung pro Element ausführen

Für Vorgänge mit Seiteneffekten (Log-Ausgabe, E-Mail-Versand usw.). Die Collection selbst wird nicht verändert.

```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("Verarbeite Bestellung #{$order['id']}", ['amount' => $order['amount']]);
    // Benachrichtigung senden, Job dispatchen usw.
});

// Rückgabe von false bricht die Schleife ab
$orders->each(function (array $order) {
    if ($order['amount'] > 10000) {
        return false; // Schleife verlassen
    }
    // ...
});
```

### `flatMap` – mappen und eine Ebene flatten

Wandelt jedes Element in ein Array um und flacht das Ergebnis um eine Ebene ab.

```php theme={null}
$users = collect([
    ['name' => 'Yamada Taro', 'tags' => ['php', 'laravel']],
    ['name' => 'Suzuki Hanako', 'tags' => ['javascript', 'vue']],
]);

// Alle Tags aller Nutzer als flache Liste
$allTags = $users->flatMap(fn ($user) => $user['tags']);
// ['php', 'laravel', 'javascript', 'vue']
```

### `reduce` – aggregieren

Reduziert die gesamte Collection auf einen einzelnen Wert.

```php theme={null}
$orders = collect([
    ['product' => 'Notebook', 'quantity' => 1, 'price' => 120000],
    ['product' => 'Maus', 'quantity' => 2, 'price' => 3500],
    ['product' => 'Tastatur', 'quantity' => 1, 'price' => 8000],
]);

// Gesamtsumme berechnen
$total = $orders->reduce(
    fn ($carry, $order) => $carry + ($order['price'] * $order['quantity']),
    0
);
// 135000
```

<Tip>
  Für einfache Summen können Sie `sum()` verwenden: `$orders->sum(fn ($o) => $o['price'] * $o['quantity'])`
</Tip>

### `reduceInto` – in ein Objekt aggregieren

Reduziert ähnlich wie `reduce`, die Callback-Funktion muss aber keinen Rückgabewert liefern. Praktisch, wenn Sie ein bestehendes Objekt direkt mutieren möchten.

```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>
  Bei `reduce` wird der Rückgabewert der Callback zum nächsten `$carry`, was sich gut für primitive Werte eignet. `reduceInto` mutiert dagegen kontinuierlich ein Objekt und führt so zu übersichtlicherem Code.
</Tip>

Möchten Sie in einen Skalar oder ein Array aggregieren, verwenden Sie in der Callback eine Referenz (`&`).

```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` – aufteilen

Zerlegt eine große Collection in Blöcke einer angegebenen Größe. Praktisch für Batch-Verarbeitung oder Anzeige.

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

// In Blöcke à 10 Einträgen teilen
$chunks = $users->chunk(10);
// $chunks->count() === 10

// Pro Batch verarbeiten
$chunks->each(function (\Illuminate\Support\Collection $batch) {
    // DB-Operationen oder E-Mail-Versand pro Batch
});
```

## Methoden-Chaining

Die eigentliche Stärke von Collections liegt im Methoden-Chaining. Mehrere Operationen lassen sich verketten, sodass komplexe Datentransformationen in einem einzigen Ausdruck möglich werden.

```php theme={null}
$orders = collect([
    ['customer' => 'Yamada Taro', 'status' => 'completed', 'amount' => 5000, 'category' => 'Elektronik'],
    ['customer' => 'Suzuki Hanako', 'status' => 'pending',   'amount' => 12000, 'category' => 'Elektronik'],
    ['customer' => 'Sato Jiro', 'status' => 'completed', 'amount' => 3500, 'category' => 'Schreibwaren'],
    ['customer' => 'Tanaka Misaki', 'status' => 'completed', 'amount' => 8000, 'category' => 'Elektronik'],
    ['customer' => 'Ito Kenichi', 'status' => 'cancelled', 'amount' => 2000, 'category' => 'Schreibwaren'],
]);

// Abgeschlossene Elektronik-Bestellungen nach Betrag absteigend sortieren und Name und Betrag extrahieren
$result = $orders
    ->filter(fn ($order) => $order['status'] === 'completed')
    ->filter(fn ($order) => $order['category'] === 'Elektronik')
    ->sortByDesc('amount')
    ->map(fn ($order) => [
        'customer' => $order['customer'],
        'amount'   => number_format($order['amount']) . ' Yen',
    ])
    ->values();

// [
//   ['customer' => 'Tanaka Misaki', 'amount' => '8,000 Yen'],
//   ['customer' => 'Yamada Taro', 'amount' => '5,000 Yen'],
// ]
```

## Zusammenspiel mit Eloquent

Ergebnisse von Eloquent-Abfragen werden stets als Instanzen von `Illuminate\Database\Eloquent\Collection` zurückgegeben.
Diese erbt von der Basis-`Collection`, sodass alle oben gezeigten Methoden zur Verfügung stehen.

```php theme={null}
use App\Models\User;
use App\Models\Order;

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

// Collection-Methoden direkt verwendbar
$adminEmails = User::all()
    ->filter(fn ($user) => $user->role === 'admin')
    ->pluck('email');

// Auch bereits geladene Relationen sind Collections
$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>
  Filterung und Sortierung, die die Datenbank effizient erledigen kann, sollten Sie über den Query Builder (`where`, `orderBy` usw.) durchführen und nicht erst auf der Collection anwenden. Andernfalls laden Sie unnötige Daten in den Speicher.
</Warning>

### Eloquent-spezifische Collection-Methoden

`Eloquent\Collection` verfügt zusätzlich über einige eigene Methoden.

```php theme={null}
$users = User::all();

// Model per ID finden
$user = $users->find(1);

// Collection der Primärschlüssel
$ids = $users->modelKeys(); // [1, 2, 3, ...]

// Relationen gebündelt nachladen
$users->load('orders', 'profile');

// Differenz- und Schnittmenge
$diff = $users->diff($otherUsers);
$intersect = $users->intersect($otherUsers);
```

## Lazy Collections

Während gewöhnliche Collections die gesamten Daten in den Arbeitsspeicher laden, nutzt eine `LazyCollection` PHP-Generatoren und verarbeitet die Daten Element für Element.
Bei mehreren Zehntausend Datensätzen oder mehr sparen Sie damit deutlich Arbeitsspeicher.

```php theme={null}
use Illuminate\Support\LazyCollection;

// Normale Collection: lädt alle Daten in den Speicher
$users = User::all(); // 100.000 Einträge landen komplett im Speicher

// LazyCollection: Element für Element
User::cursor()->each(function (User $user) {
    // Nutzer einzeln verarbeiten
    // Immer nur ein Element wird im Speicher gehalten
});
```

### LazyCollections erzeugen

```php theme={null}
use Illuminate\Support\LazyCollection;

// Aus einer Closure (Generator) erzeugen
$lazy = LazyCollection::make(function () {
    $handle = fopen('large-file.csv', 'r');

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

    fclose($handle);
});

// Die Methode cursor() von Eloquent liefert eine LazyCollection
$lazy = User::where('is_active', true)->cursor();
```

### Batch-Verarbeitung großer Datenmengen

```php theme={null}
use App\Models\Order;

// Alle Bestellungen einzeln verarbeiten (speicheroptimal)
Order::cursor()
    ->filter(fn ($order) => $order->amount > 10000)
    ->each(fn ($order) => $order->sendConfirmationEmail());

// Mit cursor() Datensatzweise abrufen und mit filter/each in einer Pipeline verarbeiten
Order::where('status', 'completed')
    ->cursor()
    ->filter(fn ($order) => $order->amount > 10000)
    ->each(fn ($order) => $order->sendConfirmationEmail());
```

<Info>
  `cursor()` holt Datensätze einzeln aus der Datenbank und spart bei großen Ergebnismengen erheblich Speicher. Beachten Sie jedoch, dass die Datenbankverbindung bis zum Ende der Verarbeitung offen bleibt.
</Info>

### Paginierung mit `take` und `skip`

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

// Die ersten 1000 überspringen, die nächsten 100 nehmen
$page = $lazy->skip(1000)->take(100)->values();
```

## Zusammenfassung

<AccordionGroup>
  <Accordion title="Häufig genutzte Methoden">
    | Methode                       | Beschreibung                                        |
    | ----------------------------- | --------------------------------------------------- |
    | `collect($array)`             | Collection erzeugen                                 |
    | `map($callback)`              | Jedes Element transformieren                        |
    | `filter($callback)`           | Nach Bedingung filtern                              |
    | `reject($callback)`           | Elemente ausschließen, die auf die Bedingung passen |
    | `first($callback)`            | Erstes Element ermitteln, das die Bedingung erfüllt |
    | `pluck($key)`                 | Werte eines bestimmten Schlüssels extrahieren       |
    | `groupBy($key)`               | Nach einem Schlüssel gruppieren                     |
    | `sortBy($key)`                | Aufsteigend sortieren                               |
    | `sortByDesc($key)`            | Absteigend sortieren                                |
    | `each($callback)`             | Verarbeitung pro Element (Seiteneffekte)            |
    | `flatMap($callback)`          | Mappen und eine Ebene flatten                       |
    | `reduce($callback, $initial)` | Aggregation zu einem Einzelwert                     |
    | `chunk($size)`                | In Blöcke einer festgelegten Größe teilen           |
    | `values()`                    | Indizes neu zuweisen                                |
    | `sum($key)`                   | Summe berechnen                                     |
    | `count()`                     | Anzahl der Elemente                                 |
  </Accordion>

  <Accordion title="Collections vs. Array-Funktionen">
    Collections lesen sich deutlich klarer als geschachtelte Array-Funktionen.

    ```php theme={null}
    // Mit Array-Funktionen
    $result = array_values(array_filter(
        array_map(fn ($u) => $u['name'], $users),
        fn ($name) => strlen($name) > 2
    ));

    // Mit einer Collection
    $result = collect($users)
        ->pluck('name')
        ->filter(fn ($name) => strlen($name) > 2)
        ->values()
        ->all();
    ```
  </Accordion>

  <Accordion title="Normale Collection vs. LazyCollection">
    * **Normale Collection**: einige Hundert bis wenige Tausend Datensätze. Einfach und intuitiv.
    * **LazyCollection**: mehrere Zehntausend Datensätze oder mehr, wenn Sie Speicher sparen möchten. In Kombination mit `cursor()` besonders wirkungsvoll.
    * `get()` liefert bei Eloquent eine normale Collection, `cursor()` eine LazyCollection.
  </Accordion>
</AccordionGroup>


## Related topics

- [Eloquent-Collections](/de/eloquent-collections.md)
- [Collection Deep Dive](/de/advanced/collection-deep-dive.md)
- [Higher-Order Messages in Collections](/de/advanced/higher-order-messages.md)
- [Eloquent-API-Ressourcen](/de/eloquent-resources.md)
- [WebSocket (Jetstream / Firehose)](/de/packages/laravel-bluesky/websocket.md)
