Base de données & ORM
La hiérarchie des entités, le patron BaseService, les migrations, et les données de départ.
Base de données & ORM
Hiérarchie des entités
Chaque entité étend BaseEntity (@mappedsuperclass, elle-même étendant cborm.models.ActiveEntity), qui ajoute automatiquement createdDate, modifiedDate, et un drapeau de suppression douce isActive à chaque table :
classDiagram
class BaseEntity {
+createdDate
+modifiedDate
+isActive
+getId()
+isLoaded()
+appendToMemento()
}
class User
class Role
class Permission
class APIToken
class RememberToken
class Passkey
class Setting
BaseEntity <|-- User
BaseEntity <|-- Role
BaseEntity <|-- Permission
BaseEntity <|-- APIToken
BaseEntity <|-- RememberToken
BaseEntity <|-- Passkey
BaseEntity <|-- Setting
User "many" --> "many" Role : roles
User "many" --> "many" Permission : à la carte
User "1" --> "many" APIToken
User "1" --> "many" RememberToken
User "1" --> "many" Passkey
Role "many" --> "many" Permission : role_permissions
dbcreate: "none" (défini dans le ormSettings de public/Application.bx) signifie que le schéma appartient exclusivement aux migrations — l'ORM ne génère ni ne modifie jamais automatiquement les tables.
Patron de la couche service
Chaque service étend BaseService (@singleton, étend cborm.models.VirtualEntityService), qui injecte qb, coldbox, wirebox, et cachebox:template, et fournit ensureSortOrder() :
component
extends="BaseService"
singleton
threadSafe
{
property name="qb" inject="provider:QueryBuilder@qb";
property name="cache" inject="cachebox:template";
function list( struct criteria = {} ){
return newCriteria()
.when( criteria.search, function( c, term ){
c.like( "name", "%#term#%" );
} )
.list();
}
}
/**
* A role: a named bundle of permissions.
*/
class extends="app.models.BaseEntity" table="roles" {
property name="roleId" fieldtype="id" generator="uuid2" ormtype="string";
property name="name" type="string";
property name="permissions"
fieldtype="many-to-many"
cfc="Permission"
linktable="role_permissions";
}
component extends="app.models.BaseService" singleton threadSafe {
function getAllForLookup(){
return newCriteria().resultTransformer( "distinct" ).list();
}
}
Migrations
Propulsées par cfmigrations via le module CLI commandbox-migrations, configuré dans .cbmigrations.json (migrationsDirectory: resources/database/migrations/, seedsDirectory: resources/database/seeds/, connexion construite à partir des mêmes variables d'environnement DB_* que public/Application.bx).
box migrate up # Run pending migrations
box migrate down # Rollback the last batch
box migrate reset # Rollback everything, then re-migrate
box migrate seed run # Run database seeders
Les migrations s'exécutent dans l'ordre du nom de fichier/horodatage :
| Migration | Crée |
|---|---|
..._settings.bx | settings (PK GUID, name unique, value en longtext) |
..._security.bx | permissions, roles, role_permissions (table de jonction à clé primaire composite, FK en cascade) |
..._users.bx | users (PK GUID, email unique, pendingEmail nullable pour les demandes de changement d'email en libre-service, password nullable, preferences en JSON, booléen hasAvatar indiquant si un utilisateur a un avatar téléversé sur le disque cbfs assets), ainsi que user_roles, user_permissions, user_remember_tokens, user_api_tokens, user_action_tokens, user_passkeys, user_sso_identities — chaque table enfant liée par FK à users.userId avec ON DELETE CASCADE (voir Sécurité & Permissions et Frontend) |
..._auditlogs.bx | audit_logs, des enregistrements d'activité en ajout seul avec sévérité/catégorie/action, métadonnées de l'acteur et de la requête, et index de requête |
Données de départ
resources/database/seeds/AdminData.bx, exécuté via box migrate seed run, crée :
- Un rôle Admin
- 20 permissions réparties sur cinq ressources (
users,roles,permissions,settings,auditlog), chacune avecread/write/delete/admin(auditlogutiliseread/export/delete/admin) — toutes assignées au rôle Admin - Un utilisateur administrateur,
admin@cbgenesis.com, assigné au rôle Admin, semé en attente de réinitialisation afin que le mot de passe public de démarrage doive être remplacé dès la première connexion
Voir Sécurité & Permissions pour savoir comment ces slugs sont imposés au niveau du handler.