vendredi 25 septembre 2026

À propos

Contact

E-commerce

Structurer un plugin d’extension WooCommerce : dossiers et bonnes pratiques

Une agence qui enchaîne les développements maison sur WooCommerce a besoin d'une arborescence reproductible. Voici un plan de dossiers et de chargement qui tient dans la durée.

Par Clément Hadrot • 13 octobre 2020 • 4 min de lecture • Aucun commentaire
Structurer un plugin d'extension WooCommerce : dossiers et bonnes pratiques

Une agence qui développe régulièrement des extensions maison pour ses clients WooCommerce finit toujours par affronter le même problème : chaque développeur range les fichiers un peu différemment, et au bout de quelques projets, plus personne ne retrouve rapidement où se trouve telle logique de calcul ou tel hook. Ce n’est pas un problème de compétence individuelle, c’est un problème d’absence de convention partagée.

Ce billet propose une arborescence de dossiers et un plan de chargement pensés spécifiquement pour une extension WooCommerce de taille moyenne — pas un micro-snippet de dix lignes, pas un plugin destiné au dépôt officiel de WordPress.org, dont la publication répond à des contraintes différentes et n’est pas traitée ici.

Le point d’entrée : un seul fichier, peu de logique

Le fichier principal du plugin, celui qui porte l’en-tête standard lu par WordPress, ne doit contenir quasiment aucune logique métier. Son unique rôle est de définir les constantes du plugin, de vérifier les prérequis, puis de déléguer à une classe principale :

/*
 * Plugin Name: Agence — Extension Boutique Client
 * Version: 1.0.0
 * Requires Plugins: woocommerce
 */

defined( 'ABSPATH' ) || exit;

define( 'AGENCE_EXT_VERSION', '1.0.0' );
define( 'AGENCE_EXT_PATH', plugin_dir_path( __FILE__ ) );
define( 'AGENCE_EXT_URL', plugin_dir_url( __FILE__ ) );

require_once AGENCE_EXT_PATH . 'includes/class-agence-extension.php';

add_action( 'plugins_loaded', array( 'Agence_Extension', 'instance' ) );

L’arborescence proposée

L'essentiel à retenir : Un point d'entrée unique évite les conflits de chargement ; Les classes se rangent par responsabilité, pas par type de fichier ; Un autoloader maison suffit sans dépendre de Composer

Plutôt que de ranger les fichiers par type technique (tous les hooks ensemble, toutes les vues ensemble), l’arborescence suivante range par responsabilité fonctionnelle, ce qui facilite la reprise du projet par un autre développeur de l’équipe :

agence-extension/
├── agence-extension.php
├── includes/
│   ├── class-agence-extension.php
│   ├── class-agence-expedition.php
│   ├── class-agence-paiement.php
│   └── class-agence-admin.php
├── templates/
│   └── emails/
│       └── notification-entrepot.php
├── assets/
│   ├── css/
│   └── js/
└── languages/
  • includes/ regroupe les classes métier, une par domaine fonctionnel plutôt qu’une par hook isolé.
  • templates/ contient les fichiers destinés à être surchargeables par le thème du client, sur le même principe que les templates de WooCommerce.
  • assets/ sépare CSS et JS chargés uniquement là où ils sont nécessaires, jamais globalement sur toutes les pages.
  • languages/ accueille les fichiers de traduction si le client en a besoin, même pour un projet livré en français uniquement au départ.

Un chargement centralisé plutôt qu’un empilement de require

La classe principale reste responsable du chargement de toutes les sous-classes et de leur instanciation, en singleton, pour éviter toute double initialisation si le fichier est chargé deux fois par accident :

class Agence_Extension {

    private static $instance = null;

    public static function instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        $this->charger_dependances();
        $this->initialiser_modules();
    }

    private function charger_dependances() {
        require_once AGENCE_EXT_PATH . 'includes/class-agence-expedition.php';
        require_once AGENCE_EXT_PATH . 'includes/class-agence-paiement.php';
        require_once AGENCE_EXT_PATH . 'includes/class-agence-admin.php';
    }

    private function initialiser_modules() {
        new Agence_Expedition();
        new Agence_Paiement();

        if ( is_admin() ) {
            new Agence_Admin();
        }
    }
}

Le test is_admin() avant d’instancier le module d’administration évite de charger inutilement des classes et des hooks côté front, ce qui a un effet mesurable sur le temps de génération d’une page produit quand le plugin grossit.

Nommer sans collision avec d’autres extensions

Sur un serveur mutualisé hébergeant plusieurs boutiques d’une même agence, deux extensions maison mal préfixées finissent tôt ou tard par déclarer la même fonction ou la même classe. Un préfixe court mais spécifique au client, ou l’usage d’un espace de noms PHP, élimine ce risque dès le départ :

namespace Agence\ClientAroa;

class Expedition {
    // ...
}

Ce qu’il ne faut pas mettre dans includes/

Un repère utile transmis aux nouveaux développeurs de l’agence : si une classe dépasse trois responsabilités distinctes, c’est le signe qu’elle doit être scindée, pas qu’elle doit rester dans un seul fichier « pour faire simple ».

Les fonctions utilitaires génériques, sans lien avec un hook WooCommerce précis, gagnent à vivre dans un fichier includes/helpers.php distinct, chargé une seule fois au tout début, plutôt que dispersées dans chaque classe métier.

Pour aller plus loin

Cette arborescence n’a rien d’universel ni d’officiel : elle reflète un compromis entre simplicité et maintenabilité pour des projets d’agence de taille moyenne, sans dépendance à Composer ni à un autoloader PSR-4 complet, volontairement écartés ici pour rester accessibles à toute l’équipe sans configuration supplémentaire. Sur un projet plus ambitieux, appuyé sur un vrai pipeline de build, l’ajout d’un autoloader standard et d’une gestion de dépendances via Composer devient pertinent, mais ce n’est plus le même niveau de projet que celui visé par ce billet.

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