El trait MacroableTrait
en Laravel nos permite agregar métodos adicionales a una clase de forma dinámica, sin tener que extenderla y desde cualquier parte de nuestra aplicación.
¿Suena complicado? Veamos un ejemplo
Class Foo { use Illuminate\Support\Traits\MacroableTrait; public static function bar() { return 'Hello World'; } }
Entonces podemos llamar al método bar() de la siguiente manera:
Foo::bar(); // returns Hello World
Ahora bien, digamos que necesitamos un método que en lugar de dar la bienvenida (Hello World
) envíe un mensaje de despedida, pero esto lo queremos lograr sin editar la clase. Para eso definimos un nuevo método haciendo uso del MacroableTrait
.
Foo::macro('baz', function () { return 'Bye'; });
Este macro se puede definir desde cualquier parte de la aplicación pero en Laravel es una buena practica declararlo dentro de un Service Provider.
¿Qué son y cómo crear tus propios service providers?
El método macro() es proporcionado por el MacroableTrait
y nos permite agregar funciones adicionales a la clase que lo implementa. Ahora podemos llamar a esta nueva función:
Foo::baz(); // Returns "Bye"
Algunas de las clases que conforma el framework Laravel implementan este trait para proporcionar una manera sencilla de agregar funcionalidades. Un ejemplo de ello es la clase Iluminate/Http/Response.
Esta técnica de macros es útil sobretodo cuando queremos agregar funcionalidad extra a clases del framework que no podemos modificar directamente puesto que se encuentran el directorio vendor y forman parte del framework y no de nuestra App.
Cómo crear un objeto «Response» personalizado
Supongamos que estamos desarrollando una API y queremos definir una estructura estándar para las respuestas que devuelven los controladores de dicha aplicación.
class UserController extends Controller { public function index() { $users = User::all(); return response()->json(['status' => 'success', 'data' => $users]); } public function show($id) { $data = User::find($id); $status = 'success'; if (!empty($data)) { $status = 'error'; } return response()->json(['status' => $status, 'data' => $data]); } }
En este caso vemos cómo esta linea de código comienza a repetirse de forma molesta:
return response()->json(['status' => $status, 'data' => $data]);
Esto eventualmente podría convertirse en un problema y será algo difícil de mantener cuando se requieran cambios en la estructura de las respuestas de nuestra API.
Como mencionaba anteriormente la clase Response hace uso del MacroableTrait
, así que podemos mejorar y limpiar un poco nuestro código simplemente definiendo un nuevo método para esta clase. Esto lo hacemos desde el método boot()
de la clase app\Providers\AppServiceProvider
public function boot() { Request::macro('api', function ($status, $data) { return response()->json(['status' => $status, 'data' => $data); }); }
Con este pequeño cambio podemos proceder a limpiar un poco el controlador:
class UserController extends Controller { public function index() { $users = User::all(); return response()->api('success', $users); } public function show($id) { $data = User::find($id); $status = 'success'; if (!empty($data)) { $status = 'error'; } return response()->api($status, $data); } }
Esto hace más legible nuestro código ya que podemos leer directamente que tipo de respuesta estamos devolviendo y a la vez hacemos esta parte del código más facil de mantener en el tiempo, ya que, cualquier cambio requerido en la estructura de cómo la data debe ser devuelta, podemos hacerla directamente en el macro que hemos creado sin tener que editar todos y cada uno de los métodos de nuestros controladores.
Si quieres aprender a crear tus propios macros y entender cómo funciona el mecanismo interno de estos, por favor revisa nuestro Curso de programación orientada a objetos.
Material relacionado
- Desarrollo de APIs con JSON y PHPUnit en Laravel
- Creación de macros en PHP usando traits, métodos estáticos y __call
- Qué son y cómo crear tus propios service providers
- Traits en PHP
- PHP Traits en Laravel
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.