Modèle de données

Tables lrvo_media et lrvo_media_references, migration de suppression de la visibilité, cycle de vie d'un fichier.

Le module crée deux tables. Elles sont créées et migrées par npx lrvo-db init, qui appelle le createTables exporté par le module. Voir lrvo-db.

Le préfixe est lrvo_media, au singulier, et non lrvo_medias_. Il est hérité de l'époque où la médiathèque vivait dans le core. Le renommer casserait les bases existantes sans rien apporter.

lrvo_media

Une ligne par fichier. La ligne porte les métadonnées, le fichier vit dans le stockage objet.

ColonneTypeRôle
idtext NOT NULL PRIMARY KEYIdentifiant du média. C'est ce que les contenus référencent.
filenametext NOT NULL UNIQUENom public, celui de l'URL. Modifiable.
storage_keytext NOT NULL UNIQUEClé de l'objet dans le stockage. Jamais exposée, jamais réécrite.
original_nametext NOT NULLNom du fichier au téléversement, conservé tel quel.
mime_typetext NOT NULLType MIME servi par la route de fichier.
size_bytesbigint NOT NULLTaille de l'objet stocké, après optimisation.
uploaded_bytext REFERENCES lrvo_auth_users(id) ON DELETE SET NULLAuteur du téléversement.
alt_texttextTexte alternatif, source de vérité pour tous les rendus.
captiontextLégende, même statut.
created_attimestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
updated_attimestamptz NOT NULL DEFAULT CURRENT_TIMESTAMPBumpé par toute édition, y compris un remplacement de fichier.

Index sur mime_type, sur uploaded_by, et sur created_at en descendant, qui est l'ordre de la médiathèque.

La suppression d'un compte utilisateur vide uploaded_by mais ne touche pas au média. Un fichier ne disparaît pas parce que la personne qui l'a déposé a quitté le projet.

Pourquoi deux noms

filename et storage_key sont indépendants. Renommer un média réécrit le premier et laisse le second intact, ce qui n'invalide aucune référence puisque les contenus référencent id. Voir Téléversement.

alt_text et caption sont la source de vérité

Ces deux colonnes ne sont pas des valeurs par défaut : ce sont les valeurs. Les blocs image du BlockEditor ne conservent pas de copie locale, ils relisent la fiche du média à chaque rendu.

Corriger un texte alternatif dans la médiathèque le corrige donc partout où l'image est utilisée, sans toucher aux contenus. C'est la contrepartie de la normalisation stricte décrite dans Intégration.

lrvo_media_references

Une ligne par lien entre un média et un contenu.

ColonneTypeRôle
media_idtext NOT NULL REFERENCES lrvo_media(id) ON DELETE RESTRICTMédia référencé.
source_typetext NOT NULLNature du contenu, chaîne libre.
source_idtext NOT NULLIdentifiant du contenu, en chaîne.
fieldtext NOT NULL DEFAULT ''Champ porteur du lien.
created_attimestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP

Clé primaire composée : (media_id, source_type, source_id, field). Index sur (source_type, source_id), qui est le sens de lecture de tous les appelants.

La restriction est le cœur de la table

ON DELETE RESTRICT est ce qui distingue cette table d'un simple journal. La base refuse la suppression d'un média encore référencé, et le module refuse en amont avec un message explicite donnant le nombre de contenus concernés.

Conséquence pour un module consommateur : détacher avant de supprimer un contenu, sans quoi la référence reste et bloque la suppression du média sans que rien n'indique pourquoi.

Valeurs de source_type et field

Ni l'une ni l'autre n'est validée. Ce sont des chaînes libres, sans registre.

source_typeÉcrit par
post@lrvo/module-posts
pages@lrvo/module-pages

L'incohérence de nombre entre les deux est historique. Elle est sans effet fonctionnel, chaque module ne lisant que ses propres lignes.

Pour field, deux familles cohabitent. Les champs déclarés y écrivent leur nom, celui que le projet a choisi en déclarant son type de contenu : @lrvo/module-posts enregistre field.name, pas le type du champ. Les images posées dans le BlockEditor y écrivent toutes la valeur fixe block_image. C'est ce qui permet de resynchroniser les blocs sans toucher aux champs, et inversement.

Migration

Une seule migration, exécutée à chaque lrvo-db init, y compris sur une base déjà à jour :

DROP INDEX IF EXISTS lrvo_media_visibility_idx;
ALTER TABLE lrvo_media DROP COLUMN IF EXISTS visibility;

La visibilité par média a été retirée. Tous les médias sont publics, et la route de fichier ne vérifie ni session ni permission. Voir Configuration.

Cycle de vie d'un fichier

ActionBaseStockage objet
TéléversementLigne insérée après l'envoi.Objet créé, supprimé si l'insertion échoue.
Renommagefilename réécrit.Inchangé.
Remplacementstorage_key, original_name, mime_type, size_bytes réécrits.Nouvel objet créé, ancien supprimé ensuite.
SuppressionLigne supprimée si aucune référence.Objet supprimé ensuite.

Les suppressions au stockage sont faites au mieux : leur échec est journalisé mais ne fait pas échouer l'opération. Un objet orphelin ne casse rien, il occupe de l'espace.

Notes

  • dropTables supprime les deux tables en cascade, dans l'ordre qui respecte la clé étrangère. Il ne supprime aucun fichier du stockage objet.
  • Aucune table ne conserve d'historique. Un remplacement de fichier écrase la ligne, l'ancien fichier est perdu.
  • La taille enregistrée est celle du fichier après optimisation, pas celle du fichier d'origine. Le poids affiché dans la médiathèque est donc le poids réellement servi.

Sur cette page