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.
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.
// De Illuminate\Foundation\Http\Kernelprotected function sendRequestThroughRouter($request){ return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());}
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.
// 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.
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().