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

# Patrón Pipeline

> Cómo funciona la clase Illuminate ipeline ipeline y el patrón para encadenar varios pasos de procesamiento.

## Qué es el patrón Pipeline

El patrón Pipeline consiste en hacer pasar un objeto (el «passable») por una serie de pipes (pasos de procesamiento) en orden. Cada pipe recibe la salida del paso anterior, la procesa y la pasa al siguiente.

En Laravel, este patrón lo implementa la clase `Illuminate\Pipeline\Pipeline` y es exactamente el mecanismo que utilizan los middlewares.

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

## Dónde lo usa el core de Laravel

El pipeline se usa mucho en el corazón de Laravel:

* **Middlewares HTTP** — `Illuminate\Foundation\Http\Kernel` hace pasar la petición por el pipeline de middlewares.
* **Comandos de consola** — procesamiento antes y después de los comandos Artisan.
* **Middlewares del Router** — aplicación de middlewares a grupos de rutas.

Veamos la parte del HTTP kernel donde se usa el pipeline.

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

## Uso básico

### send / through / thenReturn

La forma más sencilla de configurar un 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)` — indica el objeto que atravesará el pipeline.
* `through($pipes)` — indica el array de pipes.
* `thenReturn()` — ejecuta el pipeline y devuelve el resultado.

### then

Para definir el procesamiento final con un closure, usa `then()`.

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

### pipe

Para añadir pipes a posteriori, usa `pipe()`.

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

// Añade pipes adicionales según una condición
if ($needsExtra) {
    $pipeline->pipe(ExtraPipe::class);
}

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

## Pipes basados en clase

Usar clases dedicadas en vez de closures mejora la reutilización. Cada clase implementa el método `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 — cambiar el método invocado

Por defecto se llama al método `handle` del pipe, pero con `via()` puedes indicar otro nombre.

```php theme={null}
class ValidatePipe
{
    public function process(array $data, Closure $next): array
    {
        // Lógica de validación
        return $next($data);
    }
}

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

## Pasar parámetros a los pipes

Con `:` y `,` en el nombre de la clase puedes pasar parámetros al pipe. Es el mismo mecanismo que en el middleware `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 — código posterior al pipeline

Con `finally()` registras un callback que se ejecuta siempre al terminar el pipeline, tanto si hay éxito como si hay excepción.

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

## withinTransaction — ejecutar dentro de una transacción

Desde Laravel 11, puedes ejecutar todo el pipeline dentro de una transacción de base de datos.

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

Puedes indicar una conexión concreta.

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

Si alguno de los pipes lanza una excepción, se hace rollback de toda la transacción.

## Ejemplo práctico: pipeline de procesamiento de pedidos

Ejemplo que compone el procesamiento de un pedido en un e-commerce mediante varios pipes.

<Steps>
  <Step title="Crea las clases 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 insuficiente: {$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="Ejecuta el 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>

## Implementación interna de Pipeline

El método `carry()` es el corazón del pipeline. Usa `array_reduce` para plegar los pipes de derecha a izquierda y construir una cadena de closures.

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

Al invertir los pipes con `array_reverse` y pasarlos a `array_reduce`, se conserva el orden de invocación correcto. La pila de closures se apila de dentro hacia fuera para que el primer pipe se ejecute primero.

<Tip>
  El pipeline también se conoce como arquitectura «cebolla». La request atraviesa las capas de fuera adentro (empezando por el primer pipe) y la response vuelve de dentro hacia fuera. Por eso cada middleware puede añadir lógica antes y después de llamar a `$next()`.
</Tip>

## Uso combinado con el trait Macroable

La clase `Pipeline` usa el trait `Macroable`, así que puedes añadirle métodos propios.

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

## Próximos pasos

<Card title="Trait Macroable" icon="puzzle-piece" href="/es/advanced/macroable">
  Aprende cómo usar el trait Macroable para añadir métodos propios a clases existentes.
</Card>


## Related topics

- [Trait Macroable](/es/advanced/macroable.md)
- [Laravel FullFeed](/es/packages/laravel-fullfeed.md)
- [AST de PHP](/es/advanced/php-ast.md)
- [Redis](/es/redis.md)
- [Crear un agente personalizado de Boost](/es/advanced/boost-custom-agent.md)
