PrestaShop est une solution grand public, qui permet à un tout le monde de créer rapidement sa propre boutique e-commerce. Elle a l’avantage d’être Open Source, ce qui nous permet de pouvoir modifier facilement ses fonctionnalités.

Malgré que la solution soit utilisable par tous, chaque utilisateur a un besoin spécifique qui lui est propre, et PrestaShop ne peut pas intégrer l’ensemble de ces demandes dans le projet cœur sans avoir à transformer leur outil en usine à gaz.

C’est la raison pour laquelle nous allons voir ensemble comment créer votre propre module pour améliorer ou créer des fonctionnalités sur PrestaShop sans modifier le code source de PrestaShop.

Si jamais vous pensez que votre code permet de corriger un bug de PrestaShop, vous pouvez le soumettre sur le GitHub officiel ici : https://github.com/PrestaShop/PrestaShop

Les compétences nécessaires

Ce guide vise un public débutant sur PrestaShop, pour comprendre l’architecture d’un module pour y ajouter vos améliorations PrestaShop. Vous pouvez vous en inspirer pour créer votre propre module.

Cependant, il vous faudra quelques connaissances pour comprendre plus aisément ce guide.

Connaître les bases de PHP

Pour créer vos fonctionnalités, il est nécessaire d’avoir des compétences de base du langage PHP.

Le minimum est de savoir créer des fonctions, et dans un deuxième temps, de gérer les classes pour faciliter la maintenabilité du code.

Il est tout à fait possible de coder rapidement un module par quelqu’un ne maîtrisant pas ce langage. Mais pensez toujours sur le long terme lorsque vous faites des développements.

Lorsque vous pensez gagner du temps en bricolant rapidement des modifications dans votre code, vous vous faites au contraire perdre du temps dans le futur. Si vous avez à retoucher, améliorer ou même exporter cette fonctionnalité dans un autre PrestaShop, vous allez alors perdre tout le temps gagner lors de votre bricolage, car rien n’aurait été pensé pour la maintenabilité.

Il est plus intéressant de prendre plus de temps pour obtenir une fonctionnalité qui puisse être incrémentée plus tard, ce temps que vous gagnerez par rapport à un code fait « rapidement ».

Modifier un module existant demande plus de compétences

Il existe en grande différence entre créer son module (c’est à dire créer son propre code) et modifier un module existant (c’est à dire comprendre le code de quelqu’un d’autre).

Je vous recommande d’être plus expérimenté sur PrestaShop et PHP avant de vous lancer sur le module d’un tiers.

Vous ne pourrez pas tenir responsable les développeurs d’un module si vous avez vous même modifié le code.

Je vous conseille de toujours passer par un expert :

Les éléments de base pour créer une module PrestaShop

Vous pouvez retrouver tous les éléments de base dans cette vidéo, ou accéder au code plus bas dans l’article.

Les fichiers obligatoires

Pour créer un nouveau module, vous allez avoir besoin au minimum des 2 fichiers suivants :

  • Un dossier qui contient votre module
  • Un fichier PHP à la racine de ce module

Le fichier est le dossier doivent avoir le même nom, c’est obligatoire.

Exemple : Je souhaite créer un nouveau module « monmodule » , le dossier devra s’appeler « monmodule » et le fichier PHP « monmodule.php » .

Arborescence du module

Vous pouvez ensuite ajouter d’autres fichiers de contexte :

  • logo.png : c’est le logo qui sera affiché dans l’onglet « Modules » de PrestaShop
  • README.md : ce fichier vous permet de décrire le fonctionnement de votre module, d’y inscrire vos modifications. Il s’agit de votre fichier de suivi.

Le code minimal

La classe principale

De la même manière, la classe de votre fichier « monmodule.php » doit contenir le même nom que le fichier, soit « MonModule » .

<?php

class MonModule extends Module {
}

En étendant votre nouvelle classe de la classe « Module« , vous allez avoir accès aux fonctions d’installation de votre module.

Les fonctions d’installation

Déclarer les informations relatives module

Notre première fonction consiste à déclarer les informations globales du module : son nom, sa catégorie, sa version, son auteur et les informations affichées dans l’onglet des modules de PrestaShop.

public function __construct()
{
    $this->name = 'monmodule'; // Doit correspondre au nom du module, dans notre cas : monmodule
    $this->tab = 'front_office_features'; // Correspond à l'onglet de catégorisation du module, pour tous les connaitre : https://devdocs.prestashop.com/1.7/modules/creation/
    $this->version = '1.0'; // Version actuelle du module
    $this->author = 'Pierre Belin'; // L'auteur
    $this->ps_versions_compliancy = [ // Permet de limiter les versions compatibles
        'min' => '1.7',
        'max' => _PS_VERSION_
    ];
    parent::__construct();

    $this->displayName = $this->l('Mon module'); // Nom d'affichage
    $this->description = $this->l('Description du module'); // Description du module
    $this->confirmUninstall = $this->l('Êtes-vous sûr de vouloir désinstaller ce module ?');
}

Voici le résultat des informations si dessus dans l’onglet « Modules » de PrestaShop :

Affichage des informations du module dans PrestaShop

Nous retrouvons bien toutes les informations saisies dans notre constructeur, ce qui est une bonne nouvelle !

Déclarer les fonctions d’installation et de désinstallation

Pour que le module puisse s’installer et se désinstaller correctement, nous devons implémenter les deux fonctions suivantes :

public function install()
{
    if (parent::install()) {
        return true;
    }

    return false;
}

public function uninstall()
{
    if (parent::uninstall()) {
        return true;
    }

    return false;
}

Elles font appel aux fonctions « install » et « uninstall » de la classe « Module » pour mettre toutes les configurations nécessaires à l’enregistrement du module.

On peut personnaliser ces fonctions pour notamment ajouter des créations de base de données ainsi que des hooks, ce que nous verrons plus tard dans ce guide.

Ce qui nous donne comme fichier final :

<?php

if (!defined('_PS_VERSION_')) { // Permet de récupérer la version de PrestaShop
    exit;
}

class MonModule extends Module
{
    public function __construct()
    {
        $this->name = 'monmodule';
        $this->tab = 'front_office_features';
        $this->version = '1.0';
        $this->author = 'Pierre Belin';
        $this->ps_versions_compliancy = [
            'min' => '1.7',
            'max' => _PS_VERSION_
        ];

        parent::__construct();

        $this->displayName = $this->l('Mon module');
        $this->description = $this->l('Description du module');
        $this->confirmUninstall = $this->l('Êtes-vous sûr de vouloir désinstaller ce module ?');
    }

    public function install()
    {
        if (parent::install()) {
            return true;
        }

        return false;
    }

    public function uninstall()
    {
        if (parent::uninstall()) {
            return true;
        }

        return false;
    }
}

Voilà ! Notre premier module est installable.

Maintenant, nous allons voir comment ajouter des fonctionnalités !

Vous avez deux manières de modifier un comportement existant :

  • Override : modifier des fonctions existantes
  • Hooks : ajouter des nouvelles fonctionnalités en se branchant dans les fonctions de PrestaShop

Override : modifier des fonctions existantes

L’override est la manière la plus simple de changer une fonctionnalité de PrestaShop. Elle consiste à modifier les fonctions déjà existantes dans PrestaShop pour ajouter ses propres modifications.

L’utilisation de l’override provient surtout des anciennes versions de PrestaShop, à l’époque où les hooks n’étaient que très peu présents. PrestaShop fait un gros travail sur la création d’un maximum de hook pour remplacer les overrides.

Néanmoins, il n’est pas toujours possible de réaliser ses améliorations sans passer par des overrides, c’est pourquoi ils restent encore très utilisés.

Pour fonctionner, vous allez devoir respecter l’arborescence de PrestaShop, en l’implémentant dans le dossier « Override » de votre module.

Si votre fichier est au chemin /classes/controller/AdminController.php, vous devrez créer un fichier au chemin /modules/MONMODULE/override/classes/controller/AdminController.php.

Dans cet exemple, « monmodule » va modifier les 3 fichiers suivants : « AdminController« , « Address » et « AdminProductsController« . Les chemins dans le dossier correspondent exactement à ceux depuis la racine de PrestaShop.

Exemple d’arborescence d’un module contenant des overrides

Vous allez retrouver notamment 2 dossiers : classes et controllers. Ces dossiers contiennent la quasi-totalité du code métier de PrestaShop, c’est à dire des fonctions modifiables.

Si vous souhaitez modifier les fichiers de l’interface utilisateur ou front, je vous conseille de vous reporter aux hooks.

Ensuite, nous pouvons introduire le code à modifier dans ces fichiers.

Dans notre exemple, nous voulons changer la fonction « isValid » de la classe « Address« . Pour simplifier le fichier en enlevant toutes les autres fonctions, il ressemble à ceci :

<?php

class AddressCore extends ObjectModel
{
    public static function isValid($id_address)
    {
        $id_address = (int) $id_address;
        $isValid = Db::getInstance()->getValue('
            SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address a
            WHERE a.`id_address` = ' . $id_address . ' AND a.`deleted` = 0 AND a.`active` = 1
        ');

        return (bool) $isValid;
    }
}

La seule chose que nous avons à modifier est la déclaration de la classe. La classe de notre fichier override devient « Address » et l’ancienne classe « AddressCore » devient la classe étendu de notre module.

Nous allons copier ce module dans notre module pour obtenir le fichier suivant :

<?php

class Address extends AddressCore
{
    public static function isValid($id_address)
    {
        $id_address = (int) $id_address;
        $isValid = Db::getInstance()->getValue('
            SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address a
            WHERE a.`id_address` = ' . $id_address . ' AND a.`deleted` = 0 AND a.`active` = 1
        ');

        // MON CODE A AJOUTÉ
        if (true) {
            // FAIRE QUELQUECHOSE
        }

        return (bool) $isValid;
    }
}

Lorsque vous installerez votre module, le contenu du fichier sera automatiquement copier dans le répertoire override, c’est à dire /override/classes/Address.php, pour obtenir ceci :

<?php

class Address extends AddressCore
{ 
    /*
    * module: monmodule
    * date: 2020-05-10 20:41:32
    * version: 1.0
    */
    public static function isValid($id_address)
    {
        $id_address = (int) $id_address;
        $isValid = Db::getInstance()->getValue('
            SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address a
            WHERE a.`id_address` = ' . $id_address . ' AND a.`deleted` = 0 AND a.`active` = 1
        ');
        if (true) {
        }
        return (bool) $isValid;
    }
}

Votre modification fonctionne maintenant sur votre site ! Notez que les commentaires ne sont pas copiés, et que chacun fonction contient des informations sur son module d’origine.

Limites de l’override du back

Ils ne fonctionnent pas sur tous les dossiers

Comme dit précédemment, il n’est pas possible de surcharger tous les dossiers de PrestaShop, uniquement classes et overrides.

Depuis l’intégration de Symfony dans PrestaShop 1.7, un nouveau dossier, « src« , contient lui aussi du code métier.

Or, actuellement, il est impossible de faire des overrides sur ce dossier.

Les problèmes de concurrences

Première erreur à éviter : recopier l’intégralité du fichier d’origine dans le fichier du module !

Pourquoi ? Car il sera ensuite impossible de créer ou d’installer des modules qui modifie ces fichiers.

Pour comprendre ce problème, nous allons rentrer plus profondément dans le fonctionnement des overrides. PrestaShop, lors de l’installation d’un module, va copier le contenu des fonctions surchargées dans les fichiers du dossier override.

Lorsqu’un module possède un override sur la même fonction qu’un autre module, alors PrestaShop est dans une impasse. Comment concilier ces deux fonctions pour qu’elles fonctionnent pour les deux modules ? C’est impossible sans l’intervention d’un développeur.

On se retrouve alors dans un état bloquant, ou nous devons choisir entre l’un ou l’autre module, en attendant de créer une fonction qui inclue les deux fonctions des modules.

Imaginez ensuite lorsqu’on souhaite désinstaller seulement un des deux modules… C’est exactement ce que l’on souhaite éviter avec les hooks.

Hooks : ajouter des nouvelles fonctionnalités

Les hooks PrestaShop permettent de se cabler plus facilement dans le fonctionnement de PrestaShop pour ajouter des nouvelles fonctionnalités.

Contrairement à l’override, nous n’allons jamais modifier les fonctions core. Nous allons simplement ajouter notre code à une exécution spécifique dans PrestaShop.

Deuxième avantage, vous n’aurez pas à modifier toutes les fonctions PrestaShop pour qu’elles fassent appel à votre code.

Prenons pour exemple que vous voulez changer la fonction d’affichage des prix de vos produits. Imaginez tous les endroits où vous devez propager votre modification (page produit, page des catégorie, panier, tunnel de commande…).

En vous greffant sur un hook, votre amélioration sera ajoutée à tous les appels de ce hook, ce qui est un gain de temps non négligeable.

Il existe 2 types de hooks :

  • action : contient de la logique métier, ils sont associés aux hooks back
  • display : contient de l’affichage, ils sont associés aux hooks front

Nous allons continuer en découvrant comme déclarer les hooks, et ensuite comment les activer.

Vous pouvez retrouver l’intégralité des hooks disponibles par PrestaShop à ce lien : https://devdocs.prestashop.com/1.7/modules/concepts/hooks/list-of-hooks/

Nous allons commencer par expliquer le fonctionnement, et ensuite comme les activer. Vous n’aurez aucun résultat tant que vous n’aurez pas lu l’intégralité de la partie d’activation des hooks.

Se brancher sur un hook du back : actionHOOK

Pour cet exemple, nous allons partir du principe que nous voulons recevoir un email à chaque fois qu’un utilisateur crée un compte.

Le hook correspondant est : actionCustomerAccountAdd

La nomenclature consiste à ajouter « hook » devant le nom de votre hook écrit en CamelCase, ce qui donne : hookActionCustomerAccountAdd.

Ensuite, vous pouvez manipuler les paramètres liés à ce hook. Dans notre exemple, on ne peut pas deviner par magique l’identifiant du client qui a été créé.

On peut dans notre cas appeler l’attribut « newCustomer » de notre paramètre pour récupérer l’objet Client. Pour connaître les informations en paramètre, vous pouvez vous référer à la documentation.

Notez qu’il y aura toujours qu’un seul paramètre dans la fonction d’un hook sous forme de tableau, et que l’ensemble des données se trouveront dans ce tableau.

public function hookActionCustomerAccountAdd($params) {
    // CODE D'EXEMPLE ICI 
    $customer = $params['newCustomer'];
    $customer->lastname = 'TestMonModule';
    $customer->save();
}

Et c’est tout, il ne vous reste plus qu’à ajouter votre code.

Dans notre cas, on veut forcer le nom de famille de l’utilisateur (oui ça n’a pas de sens, c’est simplement pour l’exemple !).

On crée ensuite un nouveau compte utilisateur sur notre boutique PrestaShop.

Et l’on peut ensuite constater dans le front que le nom de famille a été forcé lorsque l’utilisateur est automatiquement connecté après la création de son compte.

Ainsi que dans le back !

Notre hook d’action fonctionne correctement !

Se brancher sur un hook du front : displayHOOK

Pour cet exemple, nous allons partir du principe que nous voulons ajouter du texte à tous les endroits où le prix des produits est affiché !

Le fonction est ensuite exactement le même que pour les hooks du back.

public function hookDisplayProductPriceBlock() {
    // EXEMPLE - Faire afficher un texte dans le front
    return $this->display(__FILE__, 'views/templates/admin/monaffichage.tpl');
}

Cependant, l’objectif de ce type de hook est d’afficher un contenu. Vous devez donc créer un fichier template.

Dans notre cas monaffichage.tpl

<p>MON TEXTE !</p>

Pas besoin d’aller chercher très loin pour l’exemple. A vous de le customiser à votre guise.

Vous obtiendrez l’architecture suivante :

Il s’agit de l’architecture propre de PrestaShop pour ajouter des fichiers de template dans les modules.

Vous pouvez ensuite trouver partout votre affichage alors que vous n’avez appelé qu’une fonction !

Bien plus simple que l’override, pas vrai ?

Activer ses hooks

Il ne vous reste qu’une étape pour arriver à ce comportement, activer vos hooks.

Pour activer automatiquement les hooks de votre module, il suffit d’ajouter la fonction « registerHook() » dans la fonction « install() » de votre module avec comme argument le nom du module sur lequel vous vous cablez.

public function install()
{
    $this->registerHook('actionCustomerAccountAdd');
    $this->registerHook('displayProductPriceBlock');

    if (parent::install()) {
        return true;
    }

    return false;
}

La déclaration est la même pour les 2 types de hooks.

Pour vérifier qu’un hook est bien activé, il suffit d’aller dans Apparence > Positions puis chercher le nom de son module pour voir l’ensemble des hooks auxquels il est lié.

Par défaut, seul les hooks displayHook sont affichés. Sélectionner bien « Afficher les points d’accroche invisibles » pour afficher aussi les actionHook.

Vous êtes maintenant capable de créer votre propre module et d’y ajouter vos propres fonctionnalités sur PrestaShop 🙂

Pour aller plus loin, retrouver toutes mes vidéos sur PrestaShop : https://www.youtube.com/playlist?list=PLX-zS17_7X8mDiHxs6oaF5q-XIm8bvMbT