Le WordPress d'aujourd'hui, décodé pour les développeurs

Extensions

Une colonne JSON MySQL pour un historique d’événements sans table à part

Plutôt qu'une table d'audit séparée, une colonne JSON indexable via un index généré convient à un historique consulté rarement.

Par WordPress Développement • 26 septembre 2023 • 4 min de lecture • Aucun commentaire
Une colonne JSON MySQL pour un historique d'événements sans table à part

Table d’audit séparée ou simple colonne sur la ligne existante : ce choix d’architecture s’est posé concrètement sur une extension de suivi de dossiers pour une structure d’accompagnement associatif. La solution retenue a été la seconde option, avec une colonne de type JSON qui accumule un tableau d’événements horodatés directement sur la ligne du dossier concerné.

Ce choix d’architecture n’est pas universel : il convient à un historique consulté rarement, en lecture ponctuelle depuis l’écran de détail d’un dossier, jamais agrégé sur l’ensemble de la table pour des statistiques globales. Comprendre cette limite avant de choisir cette structure évite une migration douloureuse plus tard.

Arborescence de la solution

wp_dossiers
├── id (BIGINT UNSIGNED, clé primaire)
├── titre (VARCHAR)
├── statut_courant (VARCHAR)
└── historique (JSON)
    └── [
          { "date": "2023-09-20T10:15:00Z", "statut": "ouvert", "auteur_id": 12 },
          { "date": "2023-09-25T14:30:00Z", "statut": "en_cours", "auteur_id": 7 }
        ]

Chaque changement de statut ajoute un élément au tableau JSON stocké dans la colonne historique, plutôt que d’insérer une ligne dans une table séparée dédiée à l’audit. La lecture de l’historique complet d’un dossier ne nécessite alors aucune jointure : une seule ligne contient déjà tout ce qu’il faut afficher.

La structure de la table

L'essentiel à retenir : Une colonne JSON évite une jointure pour un historique peu consulté ; Un index généré rend interrogeable un champ précis de la structure ; Ce choix devient coûteux dès que le volume de lecture augmente

La création de la table passe, comme toute structure personnalisée sous WordPress, par dbDelta() :

function dossiers_creer_table() {
    global $wpdb;
    require_once ABSPATH . 'wp-admin/includes/upgrade.php';

    $table = $wpdb->prefix . 'dossiers';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        titre VARCHAR(255) NOT NULL,
        statut_courant VARCHAR(30) NOT NULL DEFAULT 'ouvert',
        historique JSON NOT NULL,
        PRIMARY KEY (id)
    ) $charset_collate;";

    dbDelta( $sql );
}

L’ajout d’un événement se fait via la fonction MySQL JSON_ARRAY_APPEND(), qui ajoute un élément à un tableau JSON existant sans avoir à relire puis réécrire l’intégralité de la colonne côté PHP :

$wpdb->query( $wpdb->prepare(
    "UPDATE {$wpdb->prefix}dossiers
     SET historique = JSON_ARRAY_APPEND(historique, '$', JSON_OBJECT(
         'date', %s, 'statut', %s, 'auteur_id', %d
     )),
     statut_courant = %s
     WHERE id = %d",
    gmdate( 'c' ), 'en_cours', get_current_user_id(), 'en_cours', $dossier_id
) );

Rendre un champ JSON interrogeable

Interroger directement l’intérieur d’une colonne JSON avec WHERE reste possible via JSON_EXTRACT(), mais sans index dédié, MySQL doit analyser chaque ligne pour en extraire la valeur, ce qui redevient un balayage complet dès que la table grossit. Pour un besoin de recherche fréquent, un index généré (generated column) extrait une valeur précise de la structure JSON dans une colonne virtuelle indexable, disponible depuis MySQL 5.7 :

ALTER TABLE wp_dossiers
ADD COLUMN dernier_auteur_id BIGINT UNSIGNED
    GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(historique, '$[last].auteur_id'))) VIRTUAL,
ADD INDEX idx_dernier_auteur (dernier_auteur_id);

Cette colonne virtuelle se recalcule automatiquement à chaque modification de la colonne JSON source, sans espace de stockage supplémentaire en mode VIRTUAL, et devient interrogeable comme n’importe quelle colonne indexée classique.

Quand cette approche devient un problème

UsageColonne JSON adaptée ?
Afficher l’historique complet d’un seul dossierOui, une lecture suffit
Compter le nombre total de changements de statut sur tous les dossiersNon, nécessite de parcourir chaque JSON
Filtrer les dossiers modifiés par un auteur précis, à fort volumeLimité, même avec index généré
Générer un tableau de bord statistique sur l’historiqueNon, une table d’audit relationnelle devient préférable

Le repère que je donne pour ce choix d’architecture : une colonne JSON convient tant que l’historique se lit ligne par ligne, jamais quand il doit s’agréger en masse. Le jour où l’agrégation devient un besoin réel, la migration vers une table dédiée redevient nécessaire.

En résumé

Une colonne JSON avec index généré offre une solution légère pour un historique d’événements consulté rarement, en évitant la complexité d’une table d’audit séparée pour un besoin modeste. Cette architecture ne convient pas aux tables à fort volume de lecture agrégée, qui restent hors du périmètre de cette solution.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi