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

# Pipeline-Muster

> Funktionsweise der Klasse Illuminate ipeline ipeline sowie das Pipeline-Muster, mit dem Sie mehrere Verarbeitungsschritte als Kette zusammensetzen.

## Was ist das Pipeline-Muster?

Das Pipeline-Muster ist ein Entwurfsmuster, bei dem ein Verarbeitungsobjekt (das „Passable") durch eine Reihe von Pipes (Verarbeitungsschritte) geschickt wird. Jede Pipe erhält die Ausgabe der vorherigen Stufe, wendet ihre Verarbeitung an und reicht das Ergebnis an die nächste weiter.

In Laravel implementiert die Klasse `Illuminate\Pipeline\Pipeline` dieses Muster — die Middleware-Verarbeitung funktioniert exakt nach diesem Prinzip.

```
Request → [Middleware 1] → [Middleware 2] → [Controller] → Response
```

## Einsatzstellen im Laravel-Kern

Die Pipeline wird im Kern von Laravel breit eingesetzt.

* **HTTP-Middleware** — `Illuminate\Foundation\Http\Kernel` schickt Requests durch die Middleware-Pipeline
* **Console-Commands** — Vor- und Nachverarbeitung von Artisan-Befehlen
* **Router-Middleware** — Middleware-Anwendung auf Route-Gruppen

Werfen wir einen Blick darauf, wie der HTTP-Kernel die Pipeline nutzt.

```php theme={null}
// Aus Illuminate\Foundation\Http\Kernel
protected function sendRequestThroughRouter($request)
{
    return (new Pipeline($this->app))
        ->send($request)
        ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
        ->then($this->dispatchToRouter());
}
```

## Grundlegende Verwendung

### send / through / thenReturn

Die einfachste Pipeline-Konstruktion:

```php theme={null}
use Illuminate\Pipeline\Pipeline;

$result = app(Pipeline::class)
    ->send('hello')
    ->through([
        function (string $passable, Closure $next): string {
            return $next(strtoupper($passable));
        },
        function (string $passable, Closure $next): string {
            return $next($passable . '!');
        },
    ])
    ->thenReturn();

// $result === 'HELLO!'
```

* `send($passable)` — Das Objekt, das durch die Pipeline geschickt wird.
* `through($pipes)` — Das Array der Pipes.
* `thenReturn()` — Führt die Pipeline aus und gibt das Ergebnis zurück.

### then

Wollen Sie die Endverarbeitung als Closure angeben, verwenden Sie `then()`.

```php theme={null}
$result = app(Pipeline::class)
    ->send($request)
    ->through([AuthPipe::class, LogPipe::class])
    ->then(function ($request) {
        return 'processed: ' . $request;
    });
```

### pipe

Um nachträglich eine Pipe hinzuzufügen, nutzen Sie `pipe()`.

```php theme={null}
$pipeline = app(Pipeline::class)
    ->send($data)
    ->through([FirstPipe::class]);

// Bei Bedarf eine weitere Pipe einfügen
if ($needsExtra) {
    $pipeline->pipe(ExtraPipe::class);
}

$result = $pipeline->thenReturn();
```

## Klassenbasierte Pipes

Statt Closures verwenden Sie dedizierte Klassen für bessere Wiederverwendbarkeit. Jede Pipe-Klasse implementiert die Methode `handle`.

```php theme={null}
namespace App\Pipes;

use Closure;

class TrimPipe
{
    public function handle(string $passable, Closure $next): string
    {
        return $next(trim($passable));
    }
}
```

```php theme={null}
namespace App\Pipes;

use Closure;

class SanitizePipe
{
    public function handle(string $passable, Closure $next): string
    {
        $sanitized = htmlspecialchars($passable, ENT_QUOTES, 'UTF-8');

        return $next($sanitized);
    }
}
```

```php theme={null}
use App\Pipes\TrimPipe;
use App\Pipes\SanitizePipe;
use Illuminate\Pipeline\Pipeline;

$result = app(Pipeline::class)
    ->send('  <script>alert("xss")</script>  ')
    ->through([TrimPipe::class, SanitizePipe::class])
    ->thenReturn();

// $result === '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
```

## via — aufzurufende Methode ändern

Standardmäßig wird die Methode `handle` der Pipe aufgerufen. Über `via()` können Sie einen anderen Methodennamen setzen.

```php theme={null}
class ValidatePipe
{
    public function process(array $data, Closure $next): array
    {
        // Validierung
        return $next($data);
    }
}

$result = app(Pipeline::class)
    ->send($data)
    ->through([ValidatePipe::class])
    ->via('process')
    ->thenReturn();
```

## Parameter an Pipes übergeben

Sie können mit `:` und `,` am Klassennamen Parameter an Pipes übergeben — dieselbe Mechanik wie die Middleware-Syntax `throttle:60,1`.

```php theme={null}
namespace App\Pipes;

use Closure;

class LimitLengthPipe
{
    public function handle(string $passable, Closure $next, int $max = 100): string
    {
        return $next(substr($passable, 0, $max));
    }
}
```

```php theme={null}
$result = app(Pipeline::class)
    ->send($longText)
    ->through(['App\Pipes\LimitLengthPipe:50'])
    ->thenReturn();
```

## finally — Verarbeitung nach dem Ende der Pipeline

Mit `finally()` registrieren Sie einen Callback, der nach dem Pipeline-Ende garantiert ausgeführt wird — unabhängig von Erfolg oder Fehler.

```php theme={null}
$result = app(Pipeline::class)
    ->send($request)
    ->through([LogPipe::class, AuthPipe::class])
    ->finally(function ($passable) {
        // Logging oder Ressourcenfreigabe
        logger()->info('Pipeline completed', ['request' => $passable]);
    })
    ->thenReturn();
```

## withinTransaction — Ausführung in einer Transaktion

Ab Laravel 11 können Sie die gesamte Pipeline innerhalb einer Datenbanktransaktion laufen lassen.

```php theme={null}
$result = app(Pipeline::class)
    ->send($order)
    ->through([
        CreateOrderPipe::class,
        ChargePaymentPipe::class,
        SendConfirmationPipe::class,
    ])
    ->withinTransaction()
    ->thenReturn();
```

Sie können auch eine bestimmte Datenbankverbindung angeben.

```php theme={null}
->withinTransaction('mysql')
```

Wirft eine der Pipes eine Exception, wird die gesamte Transaktion zurückgerollt.

## Praxisbeispiel: Pipeline für die Bestellverarbeitung

Beispiel für die Bestellverarbeitung eines E-Commerce-Systems, aufgebaut aus mehreren Pipes.

<Steps>
  <Step title="Pipe-Klassen erstellen">
    ```php theme={null}
    namespace App\Pipes\Order;

    use App\Models\Order;
    use Closure;

    class ValidateInventoryPipe
    {
        public function handle(Order $order, Closure $next): Order
        {
            foreach ($order->items as $item) {
                if ($item->product->stock < $item->quantity) {
                    throw new \RuntimeException("Bestand unzureichend: {$item->product->name}");
                }
            }

            return $next($order);
        }
    }
    ```

    ```php theme={null}
    namespace App\Pipes\Order;

    use App\Models\Order;
    use Closure;

    class ReserveInventoryPipe
    {
        public function handle(Order $order, Closure $next): Order
        {
            foreach ($order->items as $item) {
                $item->product->decrement('stock', $item->quantity);
            }

            return $next($order);
        }
    }
    ```

    ```php theme={null}
    namespace App\Pipes\Order;

    use App\Models\Order;
    use App\Services\PaymentService;
    use Closure;

    class ProcessPaymentPipe
    {
        public function __construct(
            protected PaymentService $payment,
        ) {}

        public function handle(Order $order, Closure $next): Order
        {
            $this->payment->charge($order->total, $order->payment_token);
            $order->update(['status' => 'paid']);

            return $next($order);
        }
    }
    ```
  </Step>

  <Step title="Pipeline ausführen">
    ```php theme={null}
    use App\Models\Order;
    use App\Pipes\Order\ValidateInventoryPipe;
    use App\Pipes\Order\ReserveInventoryPipe;
    use App\Pipes\Order\ProcessPaymentPipe;
    use Illuminate\Pipeline\Pipeline;

    class OrderService
    {
        public function process(Order $order): Order
        {
            return app(Pipeline::class)
                ->send($order)
                ->through([
                    ValidateInventoryPipe::class,
                    ReserveInventoryPipe::class,
                    ProcessPaymentPipe::class,
                ])
                ->withinTransaction()
                ->thenReturn();
        }
    }
    ```
  </Step>
</Steps>

## Interner Aufbau von Pipeline

Die Methode `carry()` ist das Herzstück der Pipeline. Mit `array_reduce` werden die Pipes von rechts nach links gefaltet und zu einer verketteten Closure zusammengebaut.

```php theme={null}
// Intern in Pipeline::then()
protected function carry()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            if (is_callable($pipe)) {
                return $pipe($passable, $stack);
            } elseif (! is_object($pipe)) {
                [$name, $parameters] = $this->parsePipeString($pipe);
                $pipe = $this->getContainer()->make($name);
                $parameters = array_merge([$passable, $stack], $parameters);
            } else {
                $parameters = [$passable, $stack];
            }

            return $pipe->{$this->method}(...$parameters);
        };
    };
}
```

Durch `array_reverse` werden die Pipes umgekehrt und an `array_reduce` übergeben, damit die Aufrufreihenfolge stimmt. Der Closure-Stack wird von innen nach außen aufgebaut, sodass die erste Pipe zuerst ausgeführt wird.

<Tip>
  Pipelines werden manchmal auch „Zwiebel-Architektur" genannt. Der Request durchdringt die Schichten von außen (erste Pipe) nach innen; die Response läuft von innen nach außen zurück. Deshalb kann jede Middleware sowohl vor als auch nach `$next()` Verarbeitung einfügen.
</Tip>

## Kombination mit dem Macroable-Trait

Die `Pipeline`-Klasse nutzt den Trait `Macroable` — Sie können eigene Methoden ergänzen.

```php theme={null}
use Illuminate\Pipeline\Pipeline;

Pipeline::macro('sendThroughLogging', function (array $pipes) {
    /** @var Pipeline $this */
    return $this->through(array_map(function ($pipe) {
        return function ($passable, $next) use ($pipe) {
            logger()->debug("Entering pipe: {$pipe}");
            $result = $next($passable);
            logger()->debug("Exiting pipe: {$pipe}");
            return $result;
        };
    }, $pipes));
});

$result = app(Pipeline::class)
    ->send($data)
    ->sendThroughLogging([TrimPipe::class, SanitizePipe::class])
    ->thenReturn();
```

## Nächste Schritte

<Card title="Macroable-Trait" icon="puzzle-piece" href="/de/advanced/macroable">
  Lernen Sie, wie Sie mit dem Macroable-Trait bestehenden Klassen eigene Methoden hinzufügen.
</Card>


## Related topics

- [Macroable-Trait](/de/advanced/macroable.md)
- [Service-Container](/de/service-container.md)
- [PHP AST](/de/advanced/php-ast.md)
- [Liste unterstützter Sites - Feedable](/de/packages/feedable/drivers.md)
- [Eloquent Bootable Traits](/de/advanced/eloquent-bootable-traits.md)
