Generar catálogos (make:catalog)¶
El comando php artisan make:catalog crea un módulo de catálogo completo, listo para usar, siguiendo los patrones del boilerplate:
- Backend: migración, modelo, repository + interface, service + interface, requests (Index/Store/Update), policy, controller, permisos.
- Frontend (Inertia + React): páginas
index,form,show,columns,filtersbajoresources/js/pages/(en minúsculas), con DataTable, filtros, acciones en bloque y exportación (CSV/XLSX/JSON). - Infraestructura: rutas (en
routes/catalogs.php), entrada de menú (enresources/js/menu/generated.ts), y bindings enDomainServiceProvider.
El resultado es un módulo consistente, con permisos y policies correctamente cableados, UI coherente con Roles/Usuarios y exportación lista.
Tip: puedes ejecutar en modo "dry run" para ver el plan sin escribir archivos.
Requisitos previos¶
bootstrap/providers.phpdebe registrarApp\Providers\AuthServiceProvider::classpara que las Policies carguen. Si falta, las policies no se registran yGatedevolverá 403 aunque el usuario tenga permisos.- Asegúrate de tener
DomainServiceProviderhabilitado (viene incluido por defecto en el boilerplate) y queconfig/permissions.phpagregue los permisos deconfig/permissions/*.php. - Las páginas Inertia deben estar en
resources/js/pages/con rutas en minúsculas (p. ej.resources/js/pages/catalogs/tipo-documento/index.tsx).
Uso básico¶
php artisan make:catalog TipoDocumento \
--fields="code:string:50:unique,name:string:120,active:boolean,sort_order:int:nullable" \
--menu="Catálogos"
Opciones disponibles:
Name(argumento): Nombre del modelo en StudlyCase. Ej.:TipoDocumento.--fields: Lista separada por comas:nombre:tipo:args:flags. Ej.:code:string:50:unique.--menu: Etiqueta del grupo en el sidebar para insertar la entrada.--label: Etiqueta singular para UI (usada en títulos, descripciones de permisos y, junto con--label-plural, en el menú). Ej.:--label="Banco".--label-plural: Etiqueta plural para UI (usada en títulos de listados y en el menú). Si se omite, se pluraliza automáticamente--label.--soft-deletes: Agregadeleted_aty adapta unique awithoutTrashed()en reglas.--uuid-route: Usauuidcomo route key y lo agrega al modelo y requests.--force: Sobrescribe archivos existentes del módulo.--dry-run: Muestra un resumen (archivos a crear/modificar) sin escribir.
Sintaxis de --fields¶
- Tipos soportados:
string/varchar,int/integer,bigint,decimal(precision,scale),boolean/bool,text,enum(A,B,C)(mapeado comostring(50)en BD). - Flags:
nullable,unique. - Relaciones (formularios tipo combo/select):
- Si el campo termina en
_id(ej:nationality_id:int), el generador creaSelecten frontend y validaciónRule::exists(...)en requests. - Puedes forzar tabla/columnas con
ref=tabla,columna_valor,columna_label(también acepta|como separador). - Ejemplo:
nationality_id:int:nullable:ref=nationalities,id,name.
- Si el campo termina en
- Normalizaciones automáticas:
active→is_active,order→sort_order.
Generación schema-aware (según campos reales)¶
El generador adapta automáticamente repository, requests, service, controller, permisos y rutas según los campos que declares en --fields.
-
Si detecta campos de relación (
*_idoref=...):- En
form.tsxgenera unSelect(combo) en lugar de input texto/plano. - En el controller genera
formOptions()para cargar opciones desde BD (options.*Options). - En
StoreRequestyUpdateRequestagregaRule::exists(tabla,columna).
- En
-
Si NO existe
is_active(o aliasactive):- No se genera método
setActiveen el controller. - No se agrega permiso
.setActive. - La ruta
PATCH .../activeno se crea. - Las acciones bulk permitidas se limitan a
delete|restore|forceDelete.
- No se genera método
- Si sí existe
is_active:- Se genera
setActive. - Se agrega permiso
.setActive. - Se incluye ruta
PATCH .../active. - Bulk incluye también
setActive.
- Se genera
Además, las rutas generadas para exportación y acciones masivas incluyen rate limiting por defecto:
- Export:
throttle:exports - Bulk:
throttle:bulk
Ejemplos:
code:string:50:unique,name:string:120,active:boolean,sort_order:int:nullable
name:string:100,description:text:nullable
estado:enum(ACTIVO,INACTIVO)
precio:decimal:12,2:nullable
Archivos generados y modificados¶
Se crean:
database/migrations/*_create_{tabla}_table.phpapp/Models/{Model}.phpapp/Contracts/Repositories/{Model}RepositoryInterface.phpapp/Repositories/{Model}Repository.phpapp/Contracts/Services/{Model}ServiceInterface.phpapp/Services/{Model}Service.phpapp/Http/Requests/{Model}IndexRequest.phpapp/Http/Requests/{Model}StoreRequest.phpapp/Http/Requests/{Model}UpdateRequest.phpapp/Policies/{Model}Policy.phpapp/Http/Controllers/{Model}Controller.phpconfig/permissions/{snake}.phpresources/js/pages/catalogs/{slug}/index.tsxresources/js/pages/catalogs/{slug}/columns.tsxresources/js/pages/catalogs/{slug}/filters.tsxresources/js/pages/catalogs/{slug}/form.tsxresources/js/pages/catalogs/{slug}/show.tsx
Se modifican idempotentemente (entre marcadores):
routes/catalogs.php(grupo de rutas con middleware de permisos por operación)- Incluye
throttle:exportsenGET /exportythrottle:bulkenPOST /bulk.
- Incluye
resources/js/menu/generated.ts(entrada del menú con permiso.view)app/Providers/DomainServiceProvider.php(bindings repo y service)app/Providers/AuthServiceProvider.php(mapeoModel => Policy), para evitar 403 enauthorize()
Nota: las inserciones son idempotentes. Si eliminas manualmente líneas dentro de los marcadores, puedes re-ejecutar el comando para reinsertarlas.
Convenciones de permisos y policies¶
- Se crea
config/permissions/{snake}.phpcon permisos estándares bajocatalogs.{slug}:.view,.create,.update,.delete,.restore,.forceDelete,.exporty.setActivesolo si existeis_activeen el esquema. - La
PolicyextiendeBaseResourcePolicyconabilityPrefix = 'catalogs.{slug}'. - El middleware de rutas y la UI (botones/acciones) usan esos permisos.
Frontend (Inertia + React)¶
- Páginas bajo
resources/js/pages/catalogs/{slug}/con layout y espaciados alineados a Roles. - Los formularios (
form.tsx) y columnas (columns.tsx) se generan dinámicamente a partir de--fields:- Si defines
swift_bic:string:11:nullable, se incluye el campo en el formulario y columna si aplica. - Si NO defines
name, el formulario no lo pedirá y las columnas no lo mostrarán. - Si agregas
active:booleanse mapea ais_activey se renderiza el toggle en edición.
- Si defines
- DataTable con filtros, ordenamiento, selección, acciones masivas y export.
- Botón “Nuevo”, acciones “Editar/Eliminar/Activar” condicionadas por
auth.can[...]compartido enHandleInertiaRequests.
Beneficios¶
- Consistencia total con módulos base (Roles/Usuarios): permisos, policies, requests y rutas.
- Previene 403 habituales: registra automáticamente la
PolicyenAuthServiceProvider. - Productividad: un comando crea BE + FE + permisos + routing + menú.
- Idempotencia: inserciones en archivos existentes entre marcadores.
- Export con cabeceras amigables en español y layout moderno, listo para producción.
Ejemplos¶
# Catálogo con campos típicos y menú "Catálogos"
php artisan make:catalog TipoDocumento \
--fields="code:string:50:unique,name:string:120,active:boolean,sort_order:int:nullable" \
--menu="Catálogos"
# Con UUID en rutas y soft deletes
php artisan make:catalog Producto \
--fields="sku:string:20:unique,nombre:string:120,precio:decimal:12,2:nullable,activo:boolean" \
--uuid-route \
--soft-deletes \
--menu="Catálogos"
# Con etiquetas en español para el menú y permisos
php artisan make:catalog Bank \
--fields="code:string:20:unique,name:string:160,swift_bic:string:11:nullable,active:boolean,sort_order:int:nullable" \
--menu="Catálogos" \
--label="Banco" \
--label-plural="Bancos"
php artisan make:catalog PhoneAreaCode \
--fields="code:string:4:unique,active:boolean,sort_order:int:nullable" \
--menu="Catálogos" \
--label="Código de área" \
--label-plural="Códigos de área"
Solución de problemas¶
- 403 al editar/actualizar:
- Verifica que
App\Providers\AuthServiceProvideresté enbootstrap/providers.php. - Confirma que el usuario tiene
catalogs.{slug}.update. - Si limpiaste rutas o menú, vuelve a ejecutar el generador con
--force.
- Verifica que
- No aparece en el menú:
- Revisa
resources/js/menu/generated.tsy los marcadores. El generador reinsertará si están presentes.
- Revisa
- Rutas faltantes:
- Asegúrate de mantener los marcadores en
routes/catalogs.php.
- Asegúrate de mantener los marcadores en
- Export falla:
- Asegúrate de tener configurados los Exporters (
exporter.csv,exporter.xlsx,exporter.json) enDomainServiceProvider(ya vienen).
- Asegúrate de tener configurados los Exporters (
Flujo recomendado post-generación¶
- Ejecuta migraciones y seeders:
php artisan migrate php artisan db:seed - Inicia el server y prueba en
/catalogs/{slug}. - Ajusta columnas/validaciones en los archivos generados si es necesario.
Con este generador, crear catálogos alineados al boilerplate es cuestión de segundos, manteniendo calidad, consistencia y seguridad.