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

# Pattern Pipeline

> Découvrez le fonctionnement de la classe Illuminate ipeline ipeline et le pattern pipeline pour composer plusieurs étapes de traitement en chaîne.

## Qu'est-ce que le pattern pipeline

Le pattern pipeline fait circuler un objet à traiter (passable) au travers d'une série de pipes (étapes de traitement). Chaque pipe reçoit la sortie de l'étape précédente, y ajoute son traitement et passe le résultat au pipe suivant.

Dans Laravel, la classe `Illuminate\Pipeline\Pipeline` implémente ce pattern. Le traitement des middlewares fonctionne exactement de cette manière.

```
requête → [middleware 1] → [middleware 2] → [contrôleur] → réponse
```

## Utilisations dans le cœur de Laravel

Le pipeline est largement utilisé au cœur de Laravel.

* **Middleware HTTP** — `Illuminate\Foundation\Http\Kernel` fait passer la requête dans un pipeline de middlewares.
* **Commandes de console** — pré/post-traitement des commandes Artisan.
* **Middlewares du Router** — application des middlewares d'un groupe de routes.

Regardons comment le kernel HTTP utilise un pipeline.

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

## Utilisation de base

### send / through / thenReturn

C'est la forme la plus simple.

```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)` — spécifie l'objet à faire passer dans le pipeline.
* `through($pipes)` — spécifie le tableau de pipes.
* `thenReturn()` — exécute le pipeline et retourne le résultat.

### then

Pour spécifier le traitement final via une closure, utilisez `then()`.

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

### pipe

Pour ajouter un pipe après coup, utilisez `pipe()`.

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

// Insertion conditionnelle d'un pipe supplémentaire
if ($needsExtra) {
    $pipeline->pipe(ExtraPipe::class);
}

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

## Pipes basés sur des classes

En utilisant des classes dédiées à la place de closures, la réutilisabilité augmente. Chaque classe implémente une méthode `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 — changer la méthode appelée

Par défaut, la méthode `handle` du pipe est invoquée, mais `via()` permet d'utiliser un autre nom de méthode.

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

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

## Passer des paramètres aux pipes

Vous pouvez passer des paramètres à un pipe en utilisant `:` et `,` dans le nom de classe. C'est le même mécanisme que la syntaxe `throttle:60,1` des middlewares.

```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 — traitement après le pipeline

`finally()` permet d'enregistrer un callback exécuté systématiquement après le pipeline, qu'il ait réussi ou échoué.

```php theme={null}
$result = app(Pipeline::class)
    ->send($request)
    ->through([LogPipe::class, AuthPipe::class])
    ->finally(function ($passable) {
        // Journalisation, libération de ressources, etc.
        logger()->info('Pipeline completed', ['request' => $passable]);
    })
    ->thenReturn();
```

## withinTransaction — exécution dans une transaction

Depuis Laravel 11, le pipeline entier peut s'exécuter à l'intérieur d'une transaction de base de données.

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

Vous pouvez aussi spécifier une connexion de base de données particulière.

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

Si l'un des pipes lève une exception, toute la transaction est annulée.

## Exemple pratique : pipeline de traitement de commande

Voici un exemple de traitement de commande e-commerce structuré en plusieurs pipes.

<Steps>
  <Step title="Créer les classes de pipe">
    ```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("Stock insuffisant : {$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="Exécuter le pipeline">
    ```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>

## Implémentation interne du Pipeline

La méthode `carry()` est le cœur du pipeline. Elle utilise `array_reduce` pour replier les pipes de droite à gauche et construire une chaîne de closures.

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

En inversant l'ordre des pipes via `array_reverse` avant de les passer à `array_reduce`, l'ordre d'appel est préservé : la pile de closures se construit depuis l'intérieur, de sorte que le premier pipe s'exécute en premier.

<Tip>
  Le pipeline est aussi appelé architecture « oignon ». La requête traverse depuis la couche externe (le premier pipe) vers l'intérieur, la réponse remonte de l'intérieur vers l'extérieur. C'est pourquoi chaque middleware peut ajouter du traitement avant et après l'appel à `$next()`.
</Tip>

## Combinaison avec le trait Macroable

La classe `Pipeline` utilise le trait `Macroable`, ce qui vous permet d'ajouter vos propres méthodes.

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

## Étapes suivantes

<Card title="Trait Macroable" icon="puzzle-piece" href="/fr/advanced/macroable">
  Découvrez comment ajouter vos propres méthodes aux classes existantes avec le trait Macroable.
</Card>


## Related topics

- [Trait Macroable](/fr/advanced/macroable.md)
- [Laravel FullFeed](/fr/packages/laravel-fullfeed.md)
- [Créer un agent personnalisé pour Boost](/fr/advanced/boost-custom-agent.md)
- [Redis](/fr/redis.md)
- [Présentation du package Blaze](/fr/blog/blaze-introduction.md)
