Usando «pipelines» en Laravel, puedes enviar un objeto a través de varias clases para realizar cualquier tipo de tarea y finalmente retornar el valor resultante una vez que todas las tareas hayan sido ejecutadas.
El ejemplo más claro sobre cómo funcionan los pipelines en Laravel reside en uno de los componentes más usados del propio framework, los Middleware.
Un middleware provee un mecanismo muy conveniente para filtrar las peticiones HTTP entrantes en tu aplicación… Puedes leer Más sobre middleware en: Cómo crear y usar los Middleware en Laravel y Tipos de Middleware en Laravel
Así es como luce un middleware básico:
<?php namespace App\Http\Middleware; use Closure; class TestMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { // Here you can add your code return $next($request); } }
Estos middleware son de hecho, «pipes» o «tubos» a través de los cuales va a ser enviado un request (petición HTTP) con el fin de realizar cualquier tarea que sea necesaria. Por ejemplo, aquí puedes verificar si la petición solicita HTML o JSON, si el usuario que envía la petición está autenticado, etc.
Si le das un vistazo a la clase Illuminate\Foundation\Http\Kernel
, verás como los middleware son ejecutados utilizando una nueva instancia de la clase Pipeline
:
/** * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
El código se puede leer como: un nuevo pipeline envía el request a través de la lista de middleware y finalmente despacha el router.
No te preocupes si esto parece un poco abrumador. Vamos a tratar de aclarar estos conceptos con el siguiente ejemplo:
Trabajando en una clase que requiere ejecutar múltiples tareas
Considera este ejemplo:
Digamos que estás construyendo un foro donde la gente puede crear nuevos temas y dejar comentarios. Pero tu cliente te pide que elimines de forma automática algunas etiquetas HTML o que edites cierto contenido del texto cada vez que se registre un nuevo comentario.
Entonces, esto es lo que se te pide hacer:
- Reemplazar cada tag
<a href="..">
con texto plano - Reemplazar malas palabras con un
*
- Remover todas las etiquetas
<script>
del contenido
Probablemente termines creando una clase para manejar cada una de estas tareas:
$pipes = [ RemoveBadWords::class ReplaceLinkTags::class RemoveScriptTags::class ];
Lo que vamos a hacer ahora es enviar el texto del contenido a cada una de esas tareas y una vez que se procese enviaremos el resultado a la siguiente. Esto lo podemos hacer utilizando un nuevo Pipeline
<?php public function create(Request $request) { $pipes = [ RemoveBadWords::class, ReplaceLinkTags::class, RemoveScriptTags::class ]; $post = app(Pipeline::class) ->send($request->content) ->through($pipes) ->then(function ($content) { return Post::create(['content' => 'content']); }); // return any type of response }
Cada clase debe tener un método handle
que se va a encargar de realizar la acción. Tal vez sería una buena idea tener una interfaz que pueda ser implementada por estas clases.
<?php namespace App; use Closure; interface Pipe { public function handle($content, Closure $next); }
Si necesitas repasar tus conocimientos sobre interfaces puedes ver nuestro Curso de programación orientada a objetos con PHP.
Cada clase debería implementar dicha interfaz:
<?php namespace App; use Closure; class RemoveBadWords implements Pipe { public function handle($content, Closure $next) { // Aqui puedes realizar cualquier tarea y finalmente devolver // el contenido $content actualizado a la siguiente clase return $next($content); } }
El método usado para realizar la acción debe recibir dos parámetros, el primero será el objeto sobre el cual queremos trabajar y el segundo será la función a donde el objeto será redirigido después de ejecutar el último pipe.
¿Qué ocurre al final?
Lo que debe pasar aquí es que el contenido del post va a ser modificado por cada uno de estos $pipes
y al final, el valor resultante es lo que va a ser almacenado:
$post = app(Pipeline::class) ->send($request->all()) ->through($pipes) ->then(function ($content) { return Post::create(['content' => $content]); });
Puedes usar un nombre diferente a «handle» para el método en cada pipe. Si lo haces necesitas especificar el nombre del método usado por el Pipeline
de la siguiente manera:
app(Pipeline::class) ->send($content) ->through($pipes) ->via('customMethodName') // <---- Metodo personalizado :) ->then(function ($content) { return Post::create(['content' => $content]); });
Consideraciones
Recuerda que hay muchas maneras diferentes de resolver este tipo de problemas. El enfoque que decidas utilizar depende de ti. Pero siempre es bueno saber que tienes esta herramienta en tu arsenal que puedes usar si es necesario.
Espero que este ejemplo te de un mejor entendimiento de lo que son estos Laravel Pipelines y de cómo usarlos.
También puedes darle un vistazo a la documentación de Laravel, si quieres aprender un poco más sobre el tema: https://laravel.com/api/5.5/Illuminate/Pipeline/Pipeline.html.
Este artículo es una traducción del post original «Understanding Laravel Pipelines» publicado en medium: https://medium.com/@jeffochoa/understanding-laravel-pipelines-a7191f75c351
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.