13.x de la documentación oficial de Laravel y la implementación y las pruebas oficiales de la última versión de Framework, v13.42.0, comprobadas el 10 de noviembre de 2026. Para los fundamentos de la autorización, consulta la guía de autorización. Para la carga de proveedores, consulta autodescubrimiento de paquetes.
No depender de la clase User de la aplicación
Como ejemplo, añadimos autorización deupdate al modelo Acme\Reports\Models\Report del paquete. Se supone que la tabla acme_reports tiene una columna owner_id que guarda el identificador de autenticación del propietario. La forma de proporcionar migraciones se trata en migraciones de paquetes.
src/Models/Report.php es un modelo Eloquent normal.
App\Models\User, depende del espacio de nombres y de la estructura de modelos de la aplicación. Si solo necesitas el identificador de autenticación del usuario, usa el contrato Authenticatable de Laravel.
Coloca lo siguiente en src/Policies/ReportPolicy.php.
null, y la diferencia de tipo entre un entero obtenido de la base de datos y una cadena se resuelve comparando ambos como cadenas. En este ejemplo solo el propietario puede actualizar. En aplicaciones con varios tipos de usuario o guards, comprueba también si el identificador basta para identificar al sujeto de forma única. Si no basta, sustitúyela por una policy propia que incluya los límites de tipo de usuario o de tenant.
Authenticatable es un contrato para datos como el identificador de autenticación. No proporciona isAdministrator() ni métodos para obtener roles. Si necesitas información de permisos propia del paquete, define otro contrato y deja que la aplicación inyecte una implementación.Registrar la correspondencia en el proveedor de servicios
En elboot() de src/ReportsServiceProvider.php, registra solo los modelos que pertenecen al paquete.
Gate::policy() guarda el nombre de la clase de la policy usando como clave el nombre de la clase del modelo. El registro en sí no crea la policy, no consulta la base de datos ni evalúa a ningún usuario. La policy se crea con make() del contenedor cuando se necesita, por ejemplo en Gate::getPolicyFor(). Por eso puedes inyectar dependencias en el constructor de la policy.
La ubicación de Report y ReportPolicy también encaja con el autodescubrimiento estándar, pero aquí se registra de forma explícita para dejar claro qué correspondencia proporciona el paquete. El registro por sí solo no aplica autorización a los endpoints HTTP. Antes de la actualización, llama a lo siguiente, por ejemplo en un controlador.
AuthorizationException, que el procesamiento HTTP habitual convierte en una respuesta 403. Los argumentos del método de la policy difieren entre update, que recibe un modelo, y habilidades como create, que reciben el nombre de la clase del modelo. Sigue la forma de invocación de la guía de autorización.
Leer el orden de resolución de las policies
Gate::getPolicyFor() convierte el objetivo en su nombre de clase si es un objeto y busca en el siguiente orden. Este es el orden de selección de la policy, no el orden de ejecución de todos los hooks de autorización.
Por tanto, mientras exista un registro explícito para la misma clase
Report, cambiar solo #[UsePolicy] o la convención de nombres no la sustituye. En cambio, si la aplicación usa un modelo que hereda de Report, la policy encontrada por el atributo propio de la clase hija o por inferencia va antes que el registro explícito del padre. Compruébalo también con la clase de modelo que realmente se usa.
Sobrescribir explícitamente en la aplicación
Si vuelves a llamar apolicy() para el mismo modelo, el registro posterior reemplaza la correspondencia. Ponerlo simplemente en el boot() de otro proveedor depende del orden de carga, así que una opción es registrar un callback de arranque completado desde AppServiceProvider::boot() de la aplicación.
ApplicationReportPolicy es una policy que implementa la aplicación. Si el paquete registra en un boot() normal, este callback se ejecuta después. No es un mecanismo que tenga prioridad incondicional, incluidos los nuevos registros en otros callbacks de arranque o en proveedores cargados después. En este ejemplo, el proveedor que registra del paquete no es un proveedor diferido.
Como alternativa, puedes dejar la correspondencia sin cambios y sustituir en el contenedor el destino de resolución de ReportPolicy::class. resolvePolicy() pide al contenedor que la resuelva cada vez, pero que realmente se obtenga una instancia nueva depende del binding de esa policy. Evita diseños que obtengan información del usuario o de la petición en el constructor y la guarden en una policy singleton.
Separar los hooks que afectan a toda la autorización
En una comprobación normal de Gate, elGate::before() global se ejecuta primero. Si devuelve algo distinto de null, la evaluación de la policy no se ejecuta.
El diagrama muestra solo el camino en el que se encuentra una policy y se puede invocar el método correspondiente. Omite los hooks after globales posteriores y la vuelta a las definiciones del Gate cuando la policy no tiene el método objetivo.
No registres un Gate::before() global que permita todas las habilidades solo para implementar el trato de administrador dentro del paquete. Permitiría también la autorización de otros paquetes y de la aplicación. Un before() dentro de la policy se limita a esa policy, pero no se llama si no existe el método de la habilidad correspondiente. Si quieres que decida el método siguiente, devuelve null en lugar de false.
Además, el update() de este ejemplo exige un Authenticatable no nullable, por lo que el método de la policy no se ejecuta para invitados. Si ningún hook global sobrescribe el resultado, se deniega. Para permitir invitados, por ejemplo en una visualización pública, prepara por separado un argumento de usuario nullable y una comprobación explícita.
Probar el registro y la evaluación por separado
Se supone que usas la clase base de Testbench presentada en pruebas de paquetes y que cargasReportsServiceProvider::class en getPackageProviders(). El siguiente es un ejemplo de tests/Feature/ReportPolicyTest.php. Para los usuarios se usa el modelo de autenticación genérico de Laravel, de modo que puedes evaluar sin guardar en la base de datos.
forUser() no reescribe el usuario del Gate compartido. Crea un Gate independiente al que pasa las correspondencias, habilidades y callbacks de ese momento. Por eso, un Gate por usuario ya creado no refleja los cambios posteriores en el registro de policies del Gate compartido. Completa el registro durante el arranque y no guardes Gates por usuario en servicios de larga duración.
En la aplicación también necesitas comprobaciones con el guard y el modelo de usuario reales, y pruebas HTTP del endpoint de actualización. Las pruebas anteriores por sí solas no verifican que la ruta tenga la autorización aplicada ni el formato en que se guarda el identificador del propietario en la base de datos.
Contratos que mantener al actualizar
- ¿Se mantienen los resultados para el propietario, otros usuarios, invitados y registros sin propietario?
- ¿Coinciden los nombres de habilidad publicados con los argumentos de los métodos de la policy?
- ¿Sigue funcionando la sustitución de la policy por parte de la aplicación?
- Si admites modelos hijos, ¿es correcta la policy que realmente se selecciona para la clase hija?
- ¿Se permiten y deniegan las peticiones de actualización reales como se espera, incluidos los hooks globales de la aplicación?
Fuentes primarias consultadas
- Documentación oficial de Laravel: Autorización
- Documentación oficial de Laravel: Desarrollo de paquetes
- Framework v13.42.0: registro, resolución y forUser de Gate
- Framework v13.42.0: AuthServiceProvider, que registra Gate como singleton
- Framework v13.42.0: boot y booted de Application
- Framework v13.42.0: pruebas oficiales del Gate de autorización