Guide complet : créer un module dans Prestashop 1.7

Guide complet : créer un module dans Prestashop 1.7


Que ce soit dans le but de le vendre sur la marketplace, afin de réaliser un développement personnalisé dans votre boutique ou simplement pour comprendre les modules dans Prestashop, ce guide complet vous aidera à créer votre premier module dans la version 1.7 de Prestashop.

Petit disclaimer avant de commencer, afin d’être totalement transparent, une grande partie de cet article se basera sur la solide documentation fournie par Prestashop sur la création de module.

Cependant cette documentation a trois problèmes :

  1. Elle n’est disponible qu’en anglais
  2. Alors que certains points sont clairement expliqués, d’autres sont un peu survolés, j’essaierai de remplir les blancs
  3. Il s’agit d’une mise à jour de la documentation pour la version 1.6, il reste donc quelques parties qui parlent ou semble s’adresser à d’anciennes versions. Ici on ne parlera que de la version 1.7

Comme cet article sera plutôt conséquent, voici les points qui seront traités :

  1. Introduction
  2. Structure des dossiers et fichiers
  3. Créer son premier module
  4. Créer une vue de configuration
  5. Afficher du contenu sur votre boutique
  6. Traduire le module
  7. Conclusion

Evidemment, si vous vous attaquez à la création de module, j’imagine que vous n’êtes pas totalement débutant en PHP, que vous comprenez le fonctionnement des modèles de Prestashop et de Smarty et que vous connaissez la programmation orientée objet et le modèle MVC.

Introduction

Fonctionnement

Un module Prestashop se constitue d’un fichier PHP principal avec autant de fichiers PHP que vous jugez nécessaires, des templates (fichiers .tpl) et des ressources (CSS, JavaScript, images, polices).

Un module peut s’afficher uniquement pour le marchand dans le back office ou pour le visiteur sur la boutique, ou les deux.

Le module peut ensuite interagir avec ce que Prestashop appel les hooks (crochet) qui permet lors de l’appel d’un hook par Prestashop d’exécuter certaines parties du code de votre module.

Par exemple lorsque le hook displayHome qui affiche la page d’accueil est appelé, il est possible de lier son module à ce hook et d’afficher des informations ou de réaliser une opération à ce moment-là.

Un module peut :

  • Afficher du contenu HTML dynamique en fonction de vos besoins
  • Exécuter divers tâches (import / export, modification en masse, etc.)
  • Être configuré selon les besoins de la tâche à réaliser. Plus la configuration est complète, plus les possibilités sont grandes pour l’utilisateur
  • Ajouter des fonctionnalités à Prestashop sans toucher aux fichiers de base du logiciel, ce qui permet d’éviter les problèmes lors des mises à jour de l’application.

Si vous venez de la version 1.6 de Prestashop et que vous aimeriez connaître les changements, vous pouvez lire cet article en anglais sur le sujet.

Je n’entrerai pas dans les détails mais sachez que dans l’ensemble, bien que les changements de fond de Prestashop 1.7 sont importants, leur impact sur les modules est minime.

Emplacement

Tous les modules se trouvent dans le dossier /modules à la racine de votre installation de Prestashop.

Vous verrez en vous rendant dans ce dossier qu’il existe déjà pas mal de modules, une grande partie d’entre eux sont des modules standards installés par défaut avec Prestashop.

Liste des modules dans le dossier /modules de Prestashop
Liste des modules installés par défaut dans le dossier /modules de Prestashop

Par exemple le moyen de paiement Transfère bancaire est en fait un module appelé ps_wirepayment. Les développeurs ont pris le parti de réaliser des modules séparés pour certaines fonctionnalités afin de les extraire du cœur de l’application et de faciliter leur gestion de manière indépendante.

Note sur le cache et la documentation officielle

Je vous parlais plus haut des inconsistances de la documentation officielle sur certains sujets où, malgré la mention Prestashop 1.7, on parle encore des versions antérieures.

Dans la partie About the cache la documentation parle du dossier /cache situé à la racine de votre boutique. Dans ce dossier se trouve désormais un fichier deprecated.txt qui explique que la gestion du cache dans Prestashop 1.7 se fait à présent dans var/cache/[ENV].

*****************************
**   D E P R E C A T E D   **
*****************************

This folder has been deprecated since PrestaShop 1.7.
Make sure your code uses `var/cache/ENV` folder, please use the
constant `_PS_CACHE_DIR_` which is available everywhere.

ENV étant en fait l’environnement de travail, à savoir dev ou prod. C’est dans ce dossier que vous trouverez le fichier class_index.php où seront enregistré les liens entre les classes et les fichiers de déclaration.

En cas de problème de mise à jour de vos classes lors du développement, ce fichier peut être supprimé sans autre.

Même chose lorsque vous rencontrez des problèmes de mise à jour des templates, vous pouvez tenter de supprimer le contenu du dossier suivant :

\var\cache\dev\smarty\compile

Ce chapitre a été écrit pour l’exemple, mais je n’entrerai pas dans le détail sur toutes les informations qui ne sont pas à jour dans la documentation, je ne peux que vous conseiller de vous baser sur mon article pour être sûr. 😉

Générateur de module

Pour fonctionner avec Prestashop un module doit suivre certaines règles. Si vous désirez démarrer rapidement, vous pouvez utiliser le générateur de module mis à disposition par Prestashop. Il vous faudra un compte sur la marketplace de Prestashop pour l’utiliser.

Générateur de module mis à disposition par Prestashop
Le générateur de module mis à disposition par Prestashop pour créer un module rapidement

Il vous permettra entre autres de définir toutes les informations de base de votre module (nom, description, etc.), de définir son emplacement dans le back office et d’inscrire les hooks auxquels s’enregistrer.

Je vous conseille de l’essayer pour voir le résultat en fonction de votre choix et pour vous aider à comprendre la structure.

Mais pour débuter, il est important de bien comprendre comment fonctionne la création d’un module en le faisant de A à Z à la main.

Structure des dossiers et fichiers

Exemple de structure d’un module

Dans Prestashop 1.7 voici un exemple d’architecture pour un dossier de module :

.
├── classes
├── controllers
├── override
├── upgrade
├── vendor
└── views
    ├── css
    ├── img
    ├── js
    └── templates
├── config.xml
├── logo.png
└── nom_du_module.php
  • classes : Vous permet d’ajouter les classes spécifiques à votre module qui ne sont pas des controllers ou des overrides de classes existantes
  • controllers : Vous permet de déclarer les controllers qui seront utilisés par votre module. Le controller permet de déclarer une action qui affichera une vue à l’utilisateur, il est d’ailleurs toujours couplé à un template. Vous pouvez créer vos propres controller ou hériter de controllers existants
  • override : Permet de réécrire, en respectant la même structure de dossier, les méthodes, propriétés et fonctions de classes spécifiques à Prestashop. Généralement il est déconseillé de faire de l’override dans Prestashop, cette pratique pouvant créer des problèmes à lors de mises à jour. Il est préférable d’hériter d’une classe et d’y ajouter les méthodes nécessaires
  • upgrade : Permet de définir le comportement lors d’une mise à jour (modification des champs, ajout de hooks, etc.)
  • vendor / lib / libraries / sdk / etc. : Ces dossiers sont réservés pour les librairies externes que vous utiliserez pour vos modules
  • views : Contient à la racine les dossiers des différentes ressources du module (CSS, JavaScript, images, polices, etc.) et le dossier templates dans lequel se trouveront toutes vos vues. Pour remplacer une vue existante par votre propre vue, vous pouvez utiliser le même chemin et nom de fichier tpl que celui présent dans le dossier /views de la racine de votre installation de Prestashop
  • config.xml : Fichier créé à l’installation du module, garde toutes les informations du module en cache dans pour rendre l’affichage de la liste des modules plus rapide dans le back office.
  • logo.png : Logo affiché sur la liste des modules
  • nom_du_module.php : Fichier principal du module s’occupant de déclarer la classe principale du même nom, son initialisation et la gestion de l’installation / désinstallation. Ce fichier peut aussi contenir beaucoup d’autres éléments que nous verrons plus tard.

Le dossier modules dans /themes

Lorsque vous installez un module dans Prestashop, par défaut il est installé dans /modules uniquement.

Si vous souhaitez apporter des modifications à un module spécifiquement pour votre thème, vous pouvez copier certains de ces fichiers dans /themes/modules/[nom du module].

Par exemple, imaginons un module avec une vue spéciale se trouvant dans le dossier suivant :

/modules/modulexy/views/templates/hook/vue_speciale.tpl

Si vous souhaitez modifier le comportement de ce template, vous n’allez pas modifier directement ce fichier tpl. Car cela voudrait dire que vous modifiez le module directement, en cas de mise à jour, vos modifications seront perdues.

Au lieu de ça, créez une architecture avec un chemin identique dans le dossier /themes pointant vers un fichier du même nom :

/themes/votretheme/modules/modulexy/views/templates/hook/vue_speciale.tpl

Et copiez le contenu du vue_speciale.tpl original dans ce fichier, puis apportez-y les modifications désirées. De cette manière, celles-ci seront spécifiques à votre thème et n’altéreront pas la source du module original !

Vous verrez alors apparaître les modules spécifiques au thème actif dans la liste des modules, sous Modules du thème :

Modules du thème

Créer son premier module

L’équipe de Prestashop a défini une série de règles de codage lors du développement dans l’application ou la création de module. Avant de débuter je vous conseil de les consulter, bien qu’elles n’existent qu’en anglais.

Dossier

Pour créer votre premier module, rendez-vous dans le dossier /modules et là, créez un dossier avec le nom de votre module. Il devrait avoir le même que votre module, uniquement des caractères alpha numériques et aucun espace. Par exemple je vais créer un module appelé :

Mon module

Dans ce cas le dossier pourrait être simplement :

/modules/monmodule

Il est cependant conseillé de préfixer son module avec le nom de son entreprise par exemple, pour éviter les confusions. Dans ce cas il pourrait devenir :

/modules/ns_monmodule

Ensuite le dossier doit contenir le fichier principal du module pour être valide. Ce fichier doit être nommé comme le dossier, c’est à dire :

ns_monmodule.php

Ça y est, vous avez créé votre premier module ! Seul ce dossier et ce fichier sont obligatoires pour disposer d’un module valide. Mais on ne va pas s’arrêter là bien sûr.

Fichier principal du module

Dans ce fichier ns_monmodule.php nous allons gérer le fonctionnement de base du module :

  • Son constructeur
  • Méthode d’installation / désinstallation
  • Inscription et fonctionnement des hooks
  • Gestion des vues dans le back office

Test initial

Pour commencer, la première étape au début de chaque module est de vérifier que la constante PS_VERSION est définie :

<?php

    if (!defined('_PS_VERSION_')) {
        exit;
    }

Cette ligne de code permet de vérifier qu’une constante qui est toujours définie dans Prestashop existe et est accessible. Cela indique que l’environnement est bien chargé.

Ce code permet d’éviter à un individu malveillant d’accéder et d’exécuter ce fichier seul.

Classe principale

Ensuite on définit la classe du module, le fichier principal doit définir la classe principale et celle-ci doit être nommée de la même manière que le fichier de module et son dossier, en version Camel Case.

C’est à dire en passant les premières lettres de chaque mot en majuscule. Donc le nom de module ns_monmodule devient lorsqu’on instancie la classe :

if (!defined('_PS_VERSION_')) {
    exit;
}

class Ns_MonModule extends Module
{
}

Notez que la classe extends la classe Module, c’est à dire qu’elle hérite de celle-ci. La classe Module est la classe de base pour les modules, il est aussi possible d’hériter d’autres classes de modules comme PaymentModule, ModuleGridEngine, ModuleGrap, etc. afin de profiter des fonctionnalités qui leur sont propres.

À ce stade, si vous vous rendez dans votre back office sous Personnaliser > Catalogue de modules et que vous tapez le nom de votre module dans la recherche, ici ns_monmodule :

Affichage de votre module personnalisé dans la liste des modules de Prestashop

Celui-ci devrait déjà être visible, bien sûr, sans autre informations pour le moment.

Constructeur

Chaque classe dispose d’un constructeur. Qu’elle hérite de celui de sa classe parente ou qu’il soit clairement défini.

Cette méthode est appelée lorsqu’une classe de votre module est instancié avec le mot clé new. Lorsque le module est chargée par Prestashop, celui-ci créé une nouvelle instance de votre module.

<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

class Ns_MonModule extends Module
{
    public function __construct()
    {
        $this->name = 'ns_monmodule';
        $this->tab = 'front_office_features';
        $this->version = '1.0.0';
        $this->author = 'New Slang';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = [
            'min' => '1.7',
            'max' => _PS_VERSION_
        ];
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Module New Slang');
        $this->description = $this->l('Mon premier module super cool');

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

        if (!Configuration::get('NS_MONMODULE_PAGENAME')) {
            $this->warning = $this->l('Aucun nom fourni');
        }
    }
}

Voici en détail ce que fait la méthode __construct :

  • $this->name = 'ns_monmodule'; permet de définir le nom du module. Il s’agit d’un identifiant interne qui doit porter le même nom que le dossier du module
  • $this->tab défini à quel onglet appartient ce module dans la liste des modules, voir ici pour la liste complète des onglets existants et à quoi ils correspondent dans cette version 1.7
  • $this->version défini la version du module
  • $this->author spécifie l’auteur du module. Votre nom ou celui de votre entreprise
  • $this->need_instance indique s’il faut créer une instance du module au chargement de la liste des modules installés dans Prestashop. Une instance peut être utile si vous devez afficher un avertissement sur la page des modules par exemple.
  • $this->ps_versions_compliancy défini avec quelle version de Prestashop le module est compatible. Ici on indique de la version 1.7 à la version actuelle de Prestashop
  • $this->bootstrap = true indique qu’on utilisera le système de rendu de Bootstrap pour ce module. Ce qui fera qu’au lieu d’avoir ce genre d’affichage dans le back office :
Affichage d'un module dans le back office sans bootstrap

Ça ressemblera plutôt à ça :

Affichage d'un module dans le back office avec bootstrap
  • parent::__construct() appel le constructeur de la classe parente, à savoir Module pour exécuter la méthode constructeur de base
  • $this->displayName défini le nom affiché dans la liste des modules
  • $this->description défini la description affichée dans la liste des modules
  • $this->confirmUninstall : Message de confirmation optionnel à afficher lors de la désinstallation
  • if (!Configuration::get('NS_MONMODULE_PAGENAME'))... permet de vérifier si la valeur NS_MONMODULE_PAGENAME est configurée ou non. Nous y reviendrons

Si maintenant on recherche le nom de mon module désormais, à savoir Module New Slang voici le résultat :

Affichage de votre module personnalisé avec son nom et sa description dans la liste des modules de Prestashop
C’est déjà plus clair… ou pas !

Et vous pourrez l’installer pour le fun, mais pour le moment, il ne fera rien.

Installation / désinstallation

Par défaut, les méthodes install et uninstall appellent celles de la classe parente, ici donc si ces méthodes ne sont pas spécifiées Prestashop appel celle de la classe Module.

Voici comment appeler ces méthodes le plus simplement possible :

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

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

Sachez que ne pas définir ces méthodes ou les appeler de cette manière aura exactement le même comportement.

Prestashop les appellent simplement en renvoyant le message (true si c’est OK et false en cas d’erreur) de confirmation d’installation et désinstallation.

Nous allons compléter ces méthodes pour ajouter la valeur de configuration NS_MONMODULE_PAGENAME mentionnée plus haut et inscrire le module à des hooks spécifiques à l’installation.

public function install()
{
    if (Shop::isFeatureActive()) {
        Shop::setContext(Shop::CONTEXT_ALL);
    }

    if (!parent::install() ||
        !$this->registerHook('leftColumn') ||
        !$this->registerHook('header') ||
        !Configuration::updateValue('NS_MONMODULE_PAGENAME', 'Mentions légales')
    ) {
        return false;
    }

    return true;
}
  • if (Shop::isFeatureActive()) ... vérifie si le mode multi-boutique de Prestashop 1.7 est activé. Si c’est le cas définit le contexte pour appliquer l’installation à toutes les boutiques
  • Ensuite un test vérifie que l’installation s’est bien déroulée en récupérant le résultat de la méthode !parent::install(), en s’inscrivant au hook leftColumn, au hook header et en enregistrant la valeur NS_MONMODULE_PAGENAME. Chacune de ces actions va s’exécuter et si l’une d’elle retourne false l’installation a échouée. Dans le cas contraire l’installation a réussi

Une fois l’installation réalisée vous aurez donc inscrit votre module aux hooks leftColumn et header et créé un nouveau réglage dans la base de données appelé NS_MONMODULE_PAGENAME.

Lors de la désinstallation, pas besoin de désinscrire le module des différents hooks. Cependant il est important, pour faire une désinstallation propre, de supprimer le réglage NS_MONMODULE_PAGENAME créé dans la base.

Voici comment procéder :

public function uninstall()
{
    if (!parent::uninstall() ||
        !Configuration::deleteByName('NS_MONMODULE_PAGENAME')
    ) {
        return false;
    }

    return true;
}

Le code est assez simple, il vérifie que la méthode uninstall retourne true et idem pour la méthode de suppression du réglage appelé Configuration::deleteByName().

L’objet Configuration

Pour gérer la configuration de notre module, dans notre cas un simple champ appelé NS_MONMODULE_PAGENAME, nous avons utilisé l’objet Configuration de 3 manières :

  • Configuration::get(key) : Récupère la valeur donnée en paramètre
  • Configuration::update(key, value) : Modifie le réglage avec la clé donnée en paramètre, à la valeur spécifiée. Si le réglage n’existe pas il est automatiquement créé
  • Configuration::deleteByName(key) : Supprime le réglage de la base de données

En fait l’objet Configuration représente un accès facilité à la table ps_configuration dans laquelle la plupart des modules inscrivent leurs réglages. Elle permet d’accéder à la table et de la modifier sans écrire de requêtes SQL.

Notez que lorsque vous enregistrez un réglage, la valeur de celui-ci peut-être une chaîne de caractère, un nombre entier, un tableau, etc.

Tant que la récupération de la variable se fait correctement, basée sur son type, ça fonctionnera. Voici un exemple pour enregistrer un tableau PHP :

// Enregistre un tableau dans la table Configuration
Configuration::updateValue('NS_MONMODULE_SETTINGS', serialize(array(true, true, false)));

// Le récupère
$configuration_array = unserialize(Configuration::get('NS_MONMODULE_SETTINGS'));

Autre point important la table ps_configuration ne se limite pas à vos propres réglages, vous pouvez accéder à tous les réglages qu’elle contient de la même manière.

Par exemple si vous tapez Configuration::get('PS_LANG_DEFAULT') vous pourrez récupérer l’id de la langue par défaut de la boutique.

Pour connaître les champs disponibles, allez consulter le contenu de la table ps_configuration de votre boutique.

Ajouter une icône

Parce que jusqu’à présent, l’affichage de votre module dans la liste des modules est un peu triste et difficile à identifier au premier coup d’œil, vous pouvez y ajouter une icône pour le représenter.

Pour s’afficher sur votre module, le logo doit respecter ces points :

  • Il doit être à la racine du dossier de votre module
  • Il doit être au format PNG
  • Il doit s’appeler logo.png
  • Il doit être carré et d’une dimension minimum de 32x32px

Une fois le logo ajouté, voici un exemple de ce à quoi il va ressembler dans la liste des modules :

Affichage de votre module personnalisé avec son nom, sa description et son icône dans la liste des modules de Prestashop

Sécuriser l’accès votre module

Dernier point pour finaliser la création de ce premier module, sécuriser l’accès à son contenu. Dans la situation actuelle, si un utilisateur tape :

https://votresite.com/modules/votremodule

Il pourra potentiellement accéder à son contenu. Même si l’accès à votre module risque de ne retourner que quelques erreurs PHP, il est conseillé d’éviter ce genre de pratique.

Pour se faire, créer un fichier index.php dans chacun des dossiers et sous-dossier de votre module avec un contenu de ce type :

<?php 
     header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
     header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
     header("Cache-Control: no-store, no-cache, must-revalidate");
     header("Cache-Control: post-check=0, pre-check=0", false);
     header("Pragma: no-cache");
     header("Location: ../");
     exit;

L’idée est que l’en-tête renvoyé par PHP force l’absence de mise en cache avec les options Cache-Control, Pragma et la date d’expiration dans le passé puis redirige l’utilisateur vers la page d’accueil avec header("Location: ../").

Ce même fichier peut être utilisé dans tous vos sous-dossiers pour atteindre le même but.

Créer une vue de configuration

Pour commencer

Notre premier module a bien été créé, maintenant il faut commencer à lui donner une utilité ! Pour se faire nous allons commencer par créer un formulaire dans le back office pour gérer ses réglages.

Dans notre cas nous n’aurons qu’un seul réglage appelé NS_MONMODULE_PAGENAME qui demandera le nom d’une page qui sera affiché en lien dans la colonne de gauche de Prestashop.

Rien de bien excitant, mais ça sera suffisant pour l’exemple.

Afficher une page de configuration

Pour que Prestashop sache que votre module dispose d’une page de configuration, il faut utiliser la méthode getContent() qui retournera un formulaire accessible par l’utilisateur.

Si cette méthode existe, un bouton Configurer sera présent à droite du module, même si cette méthode ne retourne rien !

Voici le contenu de cette méthode :

public function getContent()
{
    $output = null;

    if (Tools::isSubmit('btnSubmit')) {
        $pageName = strval(Tools::getValue('NS_MONMODULE_PAGENAME'));

        if (
            !$pageName||
            empty($pageName)
        ) {
            $output .= $this->displayError($this->l('Invalid Configuration value'));
        } else {
            Configuration::updateValue('NS_MONMODULE_PAGENAME', $pageName);
            $output .= $this->displayConfirmation($this->l('Settings updated'));
        }
    }

    return $output.$this->displayForm();
}
  • Tools::isSubmit commence par vérifier si le formulaire a été envoyé ou non en fonction du nom du bouton de validation, ici appelé btnSubmit. Si ce n’est pas le cas, il affiche simplement le formulaire plus bas, sinon il gère les informations envoyées par le formulaire
  • On récupère la valeur de NS_MONMODULE_PAGENAME avec strval(Tools::getValue('NS_MONMODULE_PAGENAME'))
  • Et on teste cette valeur en regardant si elle existe et si elle n’est pas vide avec !$pageName || empty($pageName)
  • Si elle est n’est pas valide, on affiche une erreur à l’aide de la méthode displayError
  • Sinon on met à jour la valeur avec Configuration::updateValue et ensuite on affiche une confirmation de modification avec displayConfirmation
  • Pour terminer, on fait appel à la méthode displayForm (que nous allons créer par la suite) pour afficher le contenu du formulaire

Créer un formulaire

La méthode getContent() s’occupe de gérer l’affichage de la page de configuration, pour simplifier les choses nous allons gérer l’affichage du formulaire dans une méthode à part appelée displayForm.

Cette méthode utilisera la classe HelperForm qui met à disposition toute une série de méthodes pour créer et des formulaires dans le back office.

Voici à quoi ressemblera cette méthode :

public function displayForm()
{
    // Récupère la langue par défaut
    $defaultLang = (int)Configuration::get('PS_LANG_DEFAULT');
 
    // Initialise les champs du formulaire dans un tableau
    $form = array(
    'form' => array(
        'legend' => array(
            'title' => $this->l('Settings'),
        ),
        'input' => array(
            array(
                'type' => 'text',
                'label' => $this->l('Configuration value'),
                'name' => 'NS_MONMODULE_PAGENAME',
                'size' => 20,
                'required' => true
            )
        ),
        'submit' => array(
            'title' => $this->l('Save'),
            'name'  => 'btnSubmit'
        )
    ),
);
 
    $helper = new HelperForm();
 
    // Module, token et currentIndex
    $helper->module = $this;
    $helper->name_controller = $this->name;
    $helper->token = Tools::getAdminTokenLite('AdminModules');
    $helper->currentIndex = AdminController::$currentIndex.'&amp;configure='.$this->name;
 
    // Langue
    $helper->default_form_language = $defaultLang;
 
    // Charge la valeur de NS_MONMODULE_PAGENAME depuis la base
    $helper->fields_value['NS_MONMODULE_PAGENAME'] = Configuration::get('NS_MONMODULE_PAGENAME');
 
    return $helper->generateForm(array($form));
}

Voilà un gros morceau, mais ne vous inquiétez pas ce n’est pas très compliqué.

  • On commence par récupérer la langue actuelle avec Configuration::get('PS_LANG_DEFAULT') ce qui nous permettra de définir la langue du formulaire plus tard.
  • On créé les différents éléments qui composeront notre formulaire : titre (legend), champ (input) et bouton de validation (submit). Pour ce dernier élément, on lui donne explicitement le nom btnSubmit pour valider l’envoi du formulaire plus haut. Le code suivant :
'input' => array(
array(
        'type' => 'text',
        'label' => $this->l('Configuration value'),
        'name' => 'NS_MONMODULE_PAGENAME',
        'size' => 20,
        'required' => true
    )
)

Générera automatiquement ce contenu HTML une fois interprété :

<div class="form-group">
<label class="control-label col-lg-3 required">
Configuration value
</label>
<div class="col-lg-9">
<input type="text" 
                       name="NS_MONMODULE_PAGENAME"
                       id="NS_MONMODULE_PAGENAME" 
                       value="Mentions légales" 
                       class="" 
                       size="20" 
                       required="required">
</div>
</div>

Ensuite on va créer un nouvel objet HelperForm avec $helper = new HelperForm() et il recevra les champs définis en paramètre lors de la génération.

Mais avant de générer le formulaire, voici les champs définis :

  • $helper->module = $this : Spécifie le module parent de ce formulaire
  • $helper->name_controller = $this->name : Renseigne le nom du controller, ici le nom du module
  • $helper->token = Tools::getAdminTokenLite('AdminModules') : Token unique spécifique à ce formulaire (pour éviter le code malicieux). Ce token est générée grâce à la méthode getAdminTokenLite fournie par Prestashop
  • $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name : Indique la valeur de l’attribut action du formulaire, donc l’URL à laquelle soumettre le formulaire. Ici il s’agit du controller actuel avec en paramètre le nom de notre module en valeur de la clé configure. En gros, la page actuelle
  • $helper->default_form_language = $defaultLang : Défini la langue utilisée pour ce formulaire à la langue par défaut du shop récupérée plus haut
  • $helper->fields_value['NS_MONMODULE_PAGENAME'] = Configuration::get('NS_MONMODULE_PAGENAME') : Pour terminer, récupère la valeur actuelle de notre champ dans la base pour l’afficher

Dernière chose, génération du formulaire avec, en paramètre, la liste des champs à créer à l’aide de la ligne :

return $helper->generateForm(array($form));

Pour obtenir le résultat suivant :

Résultat affiché dans la page de configuration du module
Résultat affiché dans la page de configuration du module

Si vous modifiez le contenu de ce champ, vous devriez voir cette page se rafraîchir avec une confirmation de modification !

Afficher du contenu sur votre boutique

Ajouter les méthodes liées aux hooks

Pour afficher du contenu sur la boutique, nous allons utiliser les 2 hooks auxquels le module s’inscrit à l’installation.

Si on reprend le code de la méthode d’installation :

public function install()
{
    if (Shop::isFeatureActive()) {
        Shop::setContext(Shop::CONTEXT_ALL);
    }

    if (!parent::install() ||
        !$this->registerHook('leftColumn') ||
        !$this->registerHook('header') ||
        !Configuration::updateValue('NS_MONMODULE_PAGENAME', 'Mentions légales')
    ) {
        return false;
    }

    return true;
}

On retrouve les méthodes registerHook pour le hook leftColumn et header. Vous pourrez donc voir que votre module a automatiquement été greffé à ces deux hooks en vous rendant dans Personnaliser > Apparence > Positions.

Une fois le module installé, celui-ci est automatiquement greffé aux hooks correspondants

Veuillez noter que si vous cliquez sur Greffer un module en haut à droite, vous ne pourrez lier votre module qu’aux hooks que vous aurez définis.

Ajoutons donc le code suivant pour gérer l’affichage du contenu dans la colonne de gauche avec la méthode hookDisplayLeftColumn.

Si à l’enregistrement du hook son nom était leftColumn, la méthode à appeler se forme elle de hook qui préfixe chaque méthode de ce type, Display pour indiquer qu’il s’agit d’un hook d’affichage (il existe des hooks d’action qui commencent par Action) et LeftColumn qui représente le nom du hook.

public function hookDisplayLeftColumn($params)
{
    $this->context->smarty->assign([
        'ns_page_name' => Configuration::get('NS_MONMODULE_PAGENAME'),
        'ns_page_link' => $this->context->link->getModuleLink('ns_monmodule', 'display')
      ]);

      return $this->display(__FILE__, 'ns_monmodule.tpl');
}
  • Cette méthode va commencer par assigner des variables qui seront utilisables dans les modèles tpl. Grâce à la méthode $this->context->smarty->assign on peut assigner des variables à la vue.
  • Ces variables appelées ici ns_page_name et ns_page_link seront ensuite accessibles dans la vue sous la forme $ns_page_name et $ns_page_link
  • ns_page_name récupère le champ dans la table ps_configuration tel que défini dans la configuration du module
  • ns_page_link lui va récupérer, dans le contexte actuel, un lien vers une action display de notre module ns_monmodule. Nous la définirons plus loin
  • Pour terminer, $this->display s’occupe de récupérer le fichier de template ns_monmodule.tpl utilisé pour afficher le contenu

Maintenant que le hook leftColumn a été défini, définissons le hook header. Celui-ci sera exécuté dans la balise <head> du document HTML.

public function hookDisplayHeader()
{
    $this->context->controller->registerStylesheet(
        'ns_monmodule',
        $this->_path.'views/css/ns_monmodule.css',
        ['position' => 'head', 'priority' => 150]
    );
}

Le hook header est très utile pour ajouter des fichiers CSS ou JavaScript à vos pages et la méthode registerStylesheet permet de générer une ligne de type <link> pour afficher le CSS correctement en fonction du chemin donné.

Effectuer le rendu

Pour gérer l’affichage des templates, ceux-ci se trouveront dans le dossier /views/templates du module, sous l’un des 3 dossiers suivant :

  • /front : Templates liés à la boutique
  • /admin : Templates liés au back office
  • /hook : Templates lié à un hook défini (indépendant du front ou back office)

Dans notre cas, nous créerons donc le fichier suivant :

/views/templates/hook/ns_monmodule.tpl

Lorsqu’on fait appel à un modèle à partir d’un hook, Prestashop va systématiquement allez voir dans le dossier correspondant des vues si un fichier avec ce nom existe.

Si c’est le cas, il l’affiche et en interprète le contenu. Le voici :

<!-- Block ns_monmodule -->
<div id="ns_monmodule_block_home" class="block">
  <h4>{l s='New Slang Link' d='Modules.Ns_MonModule'}</h4>
  <div class="block_content">
    <a href="{$ns_page_link}">
           {if isset($ns_page_name) &amp;&amp; $ns_page_name}
               {$ns_page_name}
           {else}
               Votre lien
           {/if}
    </a>
  </div>
</div>
<!-- /Block ns_monmodule -->

Si vous voulez en savoir plus sur les modèles Prestashop et le code Smarty, consultez mon article sur la personnalisation de votre thème Prestashop ou découvrez mes 13 astuces pour modifier les vues de votre thème.

Mais rien de bien compliqué ici, il s’agit de simple HTML, agrémenté d’un peu de code Smarty :

  • {l s='New Slang Link' d='Modules.Ns_MonModule'} utilise la méthode de traduction l() en recherchant la traduction dans le contexte créé pour notre module, à savoir Modules.Ns_MonModule, pour la clé New Slang Link
  • {$ns_page_link} affiche simplement la variable assignée depuis la méthode hookDisplayLeftColumn plus haut
  • {if isset($ns_page_name) && $ns_page_name} ... affiche la variable ns_page_name si elle est définie, sinon un texte par défaut

Reste encore à créer un fichier CSS appelé ns_monmodule.css dans /views/css :

#ns_monmodule_block_home {
    box-shadow: 2px 2px 8px 0 rgba(0,0,0,.2);
    background: #fff;
    padding: 1.563rem 1.25rem;
    margin-bottom: 1.563rem;
}

Ce qui, une fois généré, donne le résultat suivant dès lors que la barre latérale est affichée :

Résultat du module affiché dans la barre latérale

Et si vous survolez le lien vous pourrez constater qu’il pointe vers l’action display de notre module qui pour le moment est introuvable. Nous allons nous occuper de ça tout de suite !

Créer un controller et une vue

Lorsque vous tentez d’accéder à cette page, Prestashop va en fait allez interpréter un controller qui se trouvera dans :

ns_monmodule/controllers/front/display.php

Normalement, un controller est une classe qui gère l’affichage d’une vue liée. Il va s’occuper de récupérer les informations dont la vue aura besoin, lui transmettre les variables et objets nécessaires et définir le template à utiliser pour l’affichage.

Si vous créez simplement un fichier display.php dans /controllers/front avec un contenu de ce genre :

Salut tout le monde les gens !

En accédant à la page vous aurez ce résultat :

Erreur affiché à l'ouverture de la page display.php

Si vous avez le mode debug de Prestashop activé, sinon l’erreur PHP ne sera pas affichée. Mais remarquez qu’il affiche le contenu et qu’ensuite il soulève une erreur indiquant qu’il ne trouve pas la classe ns_monmoduledisplayModuleFrontController.

Pour lui, l’action demandée doit donc renvoyer une classe de ce type, nous allons donc la créer en lui donnant le template à afficher.

<?php
class ns_monmoduledisplayModuleFrontController extends ModuleFrontController
{
    public function initContent()
    {
        parent::initContent();
        $this->setTemplate('module:ns_monmodule/views/templates/front/display.tpl');
    }
}

Ici on crée donc le controller, qui hérite de ModuleFrontController, et dont le contenu est généré à partir de la méthode initContent.

Celle-ci commence par exécuter la méthode de la classe parente, puis définit le template à utiliser. Ici display.tpl dans ns_monmodule/views/templates/front. Si vous créez le fichier tel quel, vous n’aurez plus d’erreur mais seulement ce contenu affiché :

Salut tout le monde les gens !

Si vous désirez faire apparaître ce contenu au sein d’une page de votre shop, avec le menu, l’en-tête, etc. vous pouvez injecter votre code dans le template page.tpl par exemple (modèle par défaut des pages) en spécifiant le block dans lequel ajouter ce contenu, ici page_content :

{extends file='page.tpl'}

{block name="page_content"}
<p>Salut tout le monde les gens !</p>
{/block}

Et voilà le résultat :

Résultat de l'affichage du controller display

Traduire le module

Dans le code

Selon la convention utilisée dans Prestashop, les textes par défaut d’un module sont toujours écris en anglais. Bien sûr ce n’est pas une obligation, mais c’est une bonne pratique.

Pour traduire une chaîne de caractère on utilise la méthode l() que vous avez déjà pu voir utiliser à plusieurs endroit dans cet article. Le fonctionnement de cette méthode varie en fonction de l’endroit où elle est utilisée.

Fichier principal du module

Au sein de la classe principale du module on appellera cette méthode de cette manière :

$this->l('Chaîne à traduire')

Ce qui indiquera à Prestashop qu’il devra rechercher la traduction avec la clé Chaîne à traduire et qui concerne le module actuel.

Controller

Dans un controller, il faudra faire référence au module de cette manière :

$this->module->l('Chaîne à traduire')

L’instance du module étant accessible à partir de la propriété module au sein d’un controller de type ModuleAdminController et ModuleFrontController.

Autres classes

Dans une autre classe il sera nécessaire de récupérer manuellement une instance du module. Voici un exemple de comment procéder :

class CustomModuleClass 
{
    public $module;
    
    public function foo()
    {
        // [...]
        $this->module = Module::getInstanceByName(<Module_name>);
        $this->text = $this->module->l('Chaîne à traduire', 'CustomModuleClass');

On récupère l’instance du module à l’aide de Module::getInstanceByName() et ensuite on appel l() sur cette instance en fournissant le nom de la classe actuel qui fait appel à la traduction.

Modèles

Dans les templates, il faudra donner le module explicitement en utilisant le contexte de notre module :

{l s='Chaîne à traduire' d='Modules.Ns_MonModule'}

s est la chaîne de caractère à traduire et d le contexte de la traduction, à savoir Modules suivi du nom de votre module.

Il est également possible d’y ajouter une hiérarchie, par exemple en créant un contexte pour la boutique et un autre pour l’administration :

{l s='Chaîne admin à traduire' d='Modules.Ns_MonModule.Admin'}
{l s='Chaîne de la boutique à traduire' d='Modules.Ns_MonModule.Front'}

Dans le back office

Lorsque vous définissez une traduction dans Prestashop, vous pouvez ensuite aller la consulter et la modifier directement dans le back office.

Un fichier généré par Prestashop s’occupe de créer la liste des traductions à partir des appels réalisés dans les différents modèles et classes de votre module (c’est aussi valide pour les modifications de modèles de votre thème par exemple).

En allant dans Personnaliser > International > Traductions vous pourrez choisir de traduire un module spécifique dans la liste :

Traduire votre module dans le back office de Prestashop

En choisissant la langue et en cliquant sur Modifier, vous verrez apparaître les différentes valeurs que vous avez défini à l’aide de la méthode l() dans vos classes et modèles :

Traduire votre module dans le back office de Prestashop

Si vous modifier une traduction et que vous l’enregistrez, comme dans cet exemple ou New Slang Link devient Autres liens, voici le résultat à l’affichage :

Résultat à l'affichage une fois la traduction effectuée

Et en plus de ça, Prestashop vous créé automatiquement un dossier translations dans le dossier de votre module, avec à l’intérieur un fichier PHP pour chaque langue traduite. Ici fr.php :

Dossier translations créé automatiquement par Prestashop

Avec ce dossier et la traduction créé, vous pouvez gérer la traduction de l’intégralité de votre module avant de le distribuer.

Conclusion

Créer un module avec Prestashop peut demander un certain temps d’adaptation, surtout concernant la structure des dossiers et fichiers (classes, controllers, etc.).

Mon conseil ? Inspirez-vous des modules existants ! Plusieurs erreurs trouvées dans la documentation officielle ont pu être corrigés pour cet article en fouillant dans les modules existants.

Par exemple la doc officielle indiquait que la traduction au sein d’un template se faisait avec le code :

{l s="Ma chaîne" mod="mon_module"}

Alors que dans Prestashop 1.7 il faut définir un contexte précis de cette manière, avec un paramètre d au lieu de mod :

 {l s="Ma chaîne" d="Modules.Mon_module} 

Bien sûr, j’avais déjà utilisé des traductions dans Prestashop 1.7 et cet attribut mod m’a tout de suite paru étrange. En consultant un des modules créés par Prestashop dans ma boutique, en gros tous les modules préfixés de ps_ dans le dossier /modules, j’ai pu clarifier certains problèmes rencontrés.

Si un temps d’adaptation est nécessaire, les possibilités sont nombreuses, la doc officielle recèle quand même beaucoup d’infos utiles et créer un module n’est finalement pas la mer à boire.

En cas de problème avec mon article, n’hésitez pas à me contacter en commentaire, j’essaierai de vous aider.

Vous n’avez plus qu’à vous y mettre 🙂


0 commentaires

Soumettre un commentaire

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

derniers articles
de mon blog

Pin It on Pinterest