vendredi 25 septembre 2026

À propos

Contact

Extensions

Colonnes calculées et triables sur un tableau d’administration maison

Trier une liste sur une valeur qui n'existe dans aucune colonne SQL directe demande un peu plus qu'un simple ORDER BY. Voici comment y arriver proprement.

Par Clément Hadrot • 28 octobre 2025 • 5 min de lecture • Aucun commentaire
Colonnes calculées et triables sur un tableau d'administration maison

Une extension de gestion de commissions pour un réseau d’agents commerciaux affichait un tableau d’administration listant chaque commande, avec une colonne « Commission nette », calculée en soustrayant les frais de retour du montant brut de commission. Le client souhaitait pouvoir trier ce tableau selon cette colonne calculée, exactement comme il pouvait déjà le faire sur la date ou le montant brut. Un simple ORDER BY classique ne pouvait pas s’appliquer directement, puisque cette valeur n’existait dans aucune colonne de la table.

Ce billet part du principe qu’un WP_List_Table de base est déjà en place, sujet traité ailleurs, pour se concentrer précisément sur ce point plus délicat : rendre triable une colonne dont la valeur n’est pas stockée telle quelle, sans dégrader les performances du tableau à mesure que le nombre de commandes augmente.

Pourquoi un tri natif ne suffit pas

La méthode prepare_items() d’un WP_List_Table classique construit généralement sa clause ORDER BY à partir du paramètre orderby transmis en URL, en le faisant correspondre directement à un nom de colonne SQL existant. Pour une colonne calculée comme la commission nette, il n’existe pas de colonne commission_nette dans la table : elle résulte d’une opération entre deux colonnes réelles, commission_brute et frais_retour.

Mapper orderby vers une expression SQL

La solution consiste à intercepter la valeur de orderby avant de construire la requête, et à la faire correspondre à une expression SQL calculée plutôt qu’à un simple nom de colonne, tout en validant strictement les valeurs acceptées pour éviter toute injection :

L'essentiel à retenir : Une valeur calculée à la volée ne peut pas se trier avec un ORDER BY natif ; Le paramètre orderby doit être mappé vers une expression SQL sécurisée ; Un index dédié évite qu'une colonne calculée ne ralentisse tout le tableau
function commissions_construire_orderby( $orderby_demande ) {
    $colonnes_autorisees = array(
        'date_commande'    => 'date_commande',
        'commission_brute' => 'commission_brute',
        'commission_nette' => '(commission_brute - frais_retour)',
    );

    return $colonnes_autorisees[ $orderby_demande ] ?? 'date_commande';
}

function commissions_recuperer_lignes( $orderby_demande, $order ) {
    global $wpdb;

    $orderby_sql = commissions_construire_orderby( $orderby_demande );
    $order_sql   = ( 'asc' === strtolower( $order ) ) ? 'ASC' : 'DESC';

    return $wpdb->get_results(
        "SELECT *, (commission_brute - frais_retour) AS commission_nette
         FROM {$wpdb->prefix}commissions_commandes
         ORDER BY $orderby_sql $order_sql
         LIMIT 20"
    );
}

Le point critique de sécurité ici est la liste blanche $colonnes_autorisees : jamais la valeur brute de orderby transmise en URL ne doit être injectée directement dans la requête SQL. Ce tableau associatif agit comme un filtre strict, où seules les clés explicitement définies peuvent produire une expression ORDER BY, toute autre valeur retombant sur un tri par défaut sûr.

Déclarer la colonne comme triable dans get_sortable_columns

Côté WP_List_Table, il suffit de déclarer la colonne calculée comme triable au même titre que les colonnes classiques, WordPress se chargeant ensuite d’afficher la flèche de tri et de construire les liens correspondants :

public function get_sortable_columns() {
    return array(
        'date_commande'    => array( 'date_commande', false ),
        'commission_brute' => array( 'commission_brute', false ),
        'commission_nette' => array( 'commission_nette', true ), // triée par défaut, ordre décroissant
    );
}

Le piège des performances sur une grande table

Sur une table de quelques centaines de commandes, cette approche ne pose aucun problème. Le client concerné en gérait plusieurs dizaines de milliers, et le tri sur l’expression calculée (commission_brute - frais_retour) empêchait MySQL d’utiliser un index existant sur l’une ou l’autre colonne individuellement, provoquant un tri complet en mémoire à chaque affichage du tableau.

La solution : une colonne générée et indexée

MySQL, à partir de la version 5.7, permet de déclarer une colonne générée, calculée automatiquement à partir d’autres colonnes de la même ligne, et de l’indexer comme n’importe quelle colonne classique :

ALTER TABLE wp_commissions_commandes
  ADD COLUMN commission_nette DECIMAL(10,2)
    GENERATED ALWAYS AS (commission_brute - frais_retour) STORED,
  ADD INDEX idx_commission_nette (commission_nette);

Avec cette colonne générée et indexée, le tri redevient un simple ORDER BY commission_nette classique, capable de s’appuyer sur l’index dédié plutôt que de recalculer et trier l’expression à chaque requête. Le fichier de mapping orderby présenté plus haut reste identique dans sa structure, seule l’expression SQL associée change, passant d’un calcul à la volée à une simple référence de colonne.

  • Pour un tableau de quelques milliers de lignes tout au plus, l’expression calculée directement dans l’ORDER BY suffit sans complexité additionnelle.
  • Au-delà, une colonne générée et indexée évite une dégradation progressive des temps de réponse à mesure que la table grossit.
  • Toute colonne générée introduite après coup nécessite une routine de migration dédiée pour les installations déjà en production, avec l’attention habituelle portée au verrouillage de table sur un ALTER TABLE volumineux.

Un repère utile avant d’ajouter une colonne calculée triable : si le tri porte sur une table qui dépasse quelques milliers de lignes, la question de l’indexation doit se poser dès la conception, pas après le premier signalement de lenteur par le client.

En résumé

Rendre triable une colonne calculée dans un tableau d’administration maison demande deux ingrédients : un mapping strict et sécurisé entre le paramètre orderby reçu et l’expression SQL réellement exécutée, et, dès que le volume de données le justifie, une colonne générée indexée plutôt qu’un calcul répété à chaque requête. Cette seconde étape, souvent négligée au départ, devient rapidement nécessaire à mesure que le tableau grossit en production.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi