response()->acmeFailure(...) sin exigir que los controladores hereden de una clase base.
Esta página se basa en la documentación oficial de Laravel en la rama predeterminada 13.x y en la implementación oficial de la última versión del Framework, v13.40.0, revisadas el 8 de noviembre de 2026. Partiendo de los fundamentos de Respuestas y del trait Macroable, trata los límites que debes tener en cuenta al mantener una macro como API pública de un paquete distribuido.
Se registra en la Factory, no en la respuesta
El nombreResponse designa tanto a una facade que solicita construir una respuesta como a la clase de la respuesta HTTP generada. Lo que extiende Response::macro() en la documentación oficial es ResponseFactory, la clase a la que se resuelve la facade.
response() sin argumentos devuelve la Factory. En cambio, response('cuerpo') y response(null) devuelven una respuesta creada con make(), así que no llaman al mismo destino.
Publicar una API de construcción para JSON de error
Registrarla en un service provider
Registra una macro con prefijo del paquete en el métodoboot() de src/HttpServiceProvider.php. Para saber cómo se carga el provider, consulta Descubrimiento de paquetes.
El contrato público de este ejemplo es un JSON con error.code y error.message, junto con un estado HTTP entre 400 y 599. Esta restricción de estado no es una limitación del mecanismo de macros de Laravel. La define el paquete como parte de una API exclusiva para errores.
runningInConsole() negada. Las pruebas de funcionalidad HTTP también se ejecutan desde la CLI.
Un provider cuya única tarea es este registro tampoco necesita ser un provider diferido que espera a que se resuelva un servicio. Para entender el registro diferido, consulta Service providers diferidos.
Llamarla desde un controlador
Después de que tu lógica de negocio detecte un conflicto, pasa el código de error elegido y un mensaje para el usuario.409 y el JSON tiene esta forma. Igual que con un json() normal, el Content-Type es application/json.
La macro solo construye las respuestas donde la llamas. No convierte automáticamente las excepciones de validación ni los errores de autenticación a este formato. Tampoco añade ningún cambio entre HTML y JSON según la cabecera
Accept.El $this de la Closure y la ruta de llamada
La Factory usa Macroable. Cuando llamas a un método no definido de una instancia, __call() busca la macro registrada y vuelve a vincular una Closure normal a la instancia sobre la que se llamó.
Aunque el código de registro esté dentro de un método del service provider, $this en tiempo de ejecución es la Factory, no el service provider. Por eso $this->json() en el ejemplo llama al método normal de la Factory.
- Define las macros que usan
$thiscon unafunction (...) { ... }normal. Unastatic functionno tiene$thisde instancia. - Si registras un objeto invocable, no se revincula como una Closure. Haz que ese objeto reciba sus dependencias por sí mismo, por ejemplo mediante su constructor.
- Los argumentos de la macro son los que pasa quien la llama. Aunque declares el tipo
Requestu otro similar, Macroable no lo inyecta automáticamente desde el contenedor.
Response::acmeFailure() parece estática, pero internamente se reenvía a la instancia de la Factory. En cambio, Illuminate\Routing\ResponseFactory::acmeFailure() sobre la clase concreta es una llamada estática real, y __callStatic() vincula la Closure sin instancia. Una macro como esta, que usa $this->json(), no admite llamadas estáticas sobre la clase concreta.
Tratar las colisiones de nombres como un problema de compatibilidad
macro() solo asigna un valor en un array estático indexado por nombre. Registrar un nombre existente no genera ninguna advertencia.
Elige un nombre que identifique tu paquete, como
acmeFailure, en lugar de uno genérico como failure. Al actualizar Laravel, comprueba también las colisiones con métodos reales mediante method_exists(ResponseFactory::class, 'acmeFailure').
Si compruebas hasMacro() y omites el registro sin condiciones, podrías usar la implementación de otro paquete como si fuera tu propia API. Solo cuando conviertas “la aplicación puede sobrescribirla registrándola antes” en parte de la especificación, publica esa prioridad junto con los argumentos y el valor de retorno necesarios. El ejemplo anterior sigue la política de registrar la definición propia del paquete cada vez que arranca el provider.
Separar el estado de registro de la vida útil de la respuesta
El registro de macros es una propiedad estática de la Factory. Volver a crear la Factory o borrar la instancia resuelta de la facade no vacía este registro. El estado de registro también puede persistir en pruebas que vuelven a crear la aplicación dentro del mismo proceso PHP. Por otro lado, la macro del ejemplo hace quejson() cree una nueva JsonResponse en cada llamada. Lo que se comparte es la lógica de construcción, no el cuerpo ni las cabeceras de la respuesta.
Estos son los límites que debes respetar en workers de larga duración.
- No captures la Request actual, el usuario autenticado ni el tenant con
use (...)en el momento del registro. Pasa esos valores como argumentos al llamar. - No reutilices una respuesta creada en el momento del registro. Devuelve una respuesta nueva cada vez.
- Si capturas configuración como valor en el momento del registro, incluye en la especificación que ese valor se conserva dentro del proceso.
- Después de actualizar el paquete, reinicia los procesos de larga duración para que se aplique el nuevo código de registro.
Probar el contrato HTTP con valores de retorno reales
En una aplicación de pruebas de Laravel 13 que pueda cargar el paquete mediante Composer, añade el siguientetests/Feature/ResponseMacroTest.php. Para Tests\TestCase y los fundamentos de las pruebas HTTP, consulta Pruebas HTTP. Para preparar un entorno de paquete independiente, consulta Pruebas de paquetes.
Response::shouldReceive('acmeFailure'), no se ejecutan el registro de la macro, la vinculación de la Closure ni la generación del JSON. Separa la verificación del número de llamadas y comprueba la respuesta HTTP real como se muestra arriba.
Además, trata el cuerpo, el estado y las cabeceras que reciben los usuarios como parte de la compatibilidad.
- Mantén
error.codeestable para la evaluación automática y distínguelo de los cambios en el mensaje mostrado. - Los nombres de los argumentos se usan como argumentos con nombre, así que comprueba el impacto de cambiarlos igual que con los valores predeterminados o el orden.
- Devuelve las respuestas sin cuerpo, como
204, con otra API comonoContent(), no como JSON de error. - Si permites extensiones que cambian el
Content-Typemediante cabeceras propias, mantenlo coherente con el formato del cuerpo. - Si tu objetivo es la serialización de datos o la paginación, considera también los recursos de API de Eloquent en lugar de reimplementarlos solo con macros.
Lista de comprobación para el mantenimiento a largo plazo
- En las combinaciones de Laravel 13 soportadas, el provider arranca y la macro se puede llamar tanto desde el helper como desde la facade.
- El nombre de la macro no colisiona con otros registros ni con métodos reales de la Factory.
- Hay pruebas de compatibilidad para las claves JSON, el estado HTTP, las cabeceras, los valores predeterminados y los argumentos con nombre.
- Las llamadas consecutivas no comparten respuestas y no se guarda información de la Request ni del usuario en el momento del registro.
- Las pruebas que reinician los registros están aisladas de otros paquetes.
- El reinicio de los procesos de larga duración forma parte del procedimiento de actualización.
Fuentes primarias consultadas
- Documentación oficial de Laravel: Response macros y JSON responses (13.x)
- Documentación oficial de Laravel: Package development (13.x)
- La facade Response y su destino de resolución (v13.40.0)
- Reenvío de la Facade a la instancia (v13.40.0)
- El helper response() (v13.40.0)
- Lógica de construcción de ResponseFactory (v13.40.0)
- Registro, vinculación y reinicio en Macroable (v13.40.0)
- Pruebas oficiales de Macroable (v13.40.0)