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

# Het Pipeline-patroon

> Hoe de klasse Illuminate\Pipeline\Pipeline werkt en hoe je met het pipelinepatroon meerdere verwerkingsstappen als een keten opbouwt.

## Wat is het pipelinepatroon?

Het pipelinepatroon is een designpattern waarbij een te verwerken object (de passable) achtereenvolgens door een reeks pipes (verwerkingsstappen) wordt geleid. Elke pipe ontvangt de uitvoer van de vorige stap, voegt bewerking toe en geeft het door aan de volgende stap.

In Laravel implementeert de klasse `Illuminate\Pipeline\Pipeline` dit patroon, en de verwerking van middleware werkt precies volgens dit mechanisme.

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

## Waar het in de Laravel-core wordt gebruikt

De pipeline wordt breed ingezet in de kern van Laravel.

* **HTTP-middleware** — `Illuminate\Foundation\Http\Kernel` leidt de request door de middleware-pipeline
* **Consolecommando's** — voor- en nabewerking van Artisan-commando's
* **Middleware van de router** — het toepassen van middleware op routegroepen

Laten we kijken naar het deel waar de HTTP-kernel de pipeline gebruikt.

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

## Basisgebruik

### send / through / thenReturn

De eenvoudigste opbouw van een pipeline.

```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)` — geeft het object op dat door de pipeline gaat
* `through($pipes)` — geeft de array van pipes op
* `thenReturn()` — voert de pipeline uit en geeft het resultaat terug

### then

Wil je de laatste bewerking als closure opgeven, dan gebruik je `then()`.

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

### pipe

Om achteraf pipes toe te voegen, gebruik je `pipe()`.

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

// Afhankelijk van de voorwaarde een extra pipe invoegen
if ($needsExtra) {
    $pipeline->pipe(ExtraPipe::class);
}

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

## Klassegebaseerde pipes

Door dedicated klassen te gebruiken in plaats van closures, verhoog je de herbruikbaarheid. Elke pipe-klasse implementeert een `handle`-methode.

```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 — de aan te roepen methode wijzigen

Standaard wordt de `handle`-methode van de pipe aangeroepen, maar met `via()` kun je een andere methodenaam opgeven.

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

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

## Parameters doorgeven aan pipes

Met `:` en `,` in de klassenaam kun je parameters aan een pipe doorgeven. Dit is hetzelfde mechanisme als de middleware-syntaxis zoals `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 — verwerking na afloop van de pipeline

Met de `finally()`-methode registreer je een callback die na afloop van de pipeline altijd wordt uitgevoerd — ongeacht succes of falen.

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

## withinTransaction — uitvoeren binnen een transactie

Sinds Laravel 11 kun je de hele pipeline binnen een databasetransactie uitvoeren.

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

Je kunt ook een specifieke databaseverbinding opgeven.

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

Als er in een van de pipes een exception optreedt, wordt de hele transactie teruggedraaid.

## Praktijkvoorbeeld: een orderverwerkingspipeline

Een voorbeeld waarin de orderverwerking van een webshop uit meerdere pipes is opgebouwd.

<Steps>
  <Step title="Maak de pipe-klassen">
    ```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("Onvoldoende voorraad: {$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="Voer de pipeline uit">
    ```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>

## De interne implementatie van Pipeline

De `carry()`-methode is het hart van de pipeline. Met `array_reduce` worden de pipes van rechts naar links gevouwen tot een keten van closures.

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

Door de pipes met `array_reverse` om te keren en aan `array_reduce` door te geven, blijft de aanroepvolgorde correct. De closure-stack wordt van binnen naar buiten opgebouwd, zodat de eerste pipe als eerste wordt uitgevoerd.

<Tip>
  De pipeline wordt ook wel de "ui-architectuur" genoemd. De request gaat van de buitenste laag (de eerste pipe) naar binnen, en de response komt van binnen weer naar buiten. Daarom kan elke middleware zowel vóór als ná het aanroepen van `$next()` bewerkingen toevoegen.
</Tip>

## Combineren met de Macroable-trait

Omdat de `Pipeline`-klasse de `Macroable`-trait gebruikt, kun je er eigen methodes aan toevoegen.

```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();
```

## Volgende stap

<Card title="Macroable-trait" icon="puzzle-piece" href="/nl/advanced/macroable">
  Leer hoe je met de Macroable-trait eigen methodes toevoegt aan bestaande klassen.
</Card>


## Related topics

- [Macroable-trait](/nl/advanced/macroable.md)
- [Laravel FullFeed](/nl/packages/laravel-fullfeed.md)
- [Introductie React — de basis voor Inertia × Laravel](/nl/blog/react-introduction.md)
- [Introductie Svelte — de basis voor Inertia × Laravel](/nl/blog/svelte-introduction.md)
- [Introductie Vue.js — de basis voor Inertia × Laravel](/nl/blog/vue-introduction.md)
