Dans cet article, nous allons voir comment exploiter à fond les trois composantes du design pattern MVC, ainsi que l’utilisation de quelques unes des nombreuses nouveautés de Prestashop 1.5 pour :
- Créer un modèle objet, qui sera stocké dans la base de données
- Gérer les vues entièrement en Smarty. Bye bye le code HTML dans le code PHP.
- Utiliser les Controllers aussi bien pour gérer les page de back office et de front office.
Spécifications
Le module Prestashop que nous allons créer permet aux utilisateurs de donner leur avis sur le site, au travers d’un formulaire sur une page dédiée du front office. Les utilisateurs peuvent choisir dans une liste de valeurs, qui sera stockée en base de données. Dans le back office, un onglet permet de valider les avis des clients, afin de les laisser apparaître sur la page du front office.
Structure du module
La structure des modules sous PrestaShop 1.5 impose une structure plus stricte.
- /controllers accueille les Controllers de back office et de front office
- /views accueille les vues Smarty pour les Controllers ainsi que les hooks
- /models n’est pas obligatoire, mais nous allons y placer les modèles objet
La classe du module
Pour commencer, nous allons créer la classe du module de manière classique.
class PrCustomerOpinion extends Module { public function __construct() { $this->name = 'prcustomeropinion'; $this->tab = 'front_office_features'; $this->version = 1.0; $this->author = '...'; $this->displayName = $this->l('Customer Opinion'); $this->description = $this->l('Example module for PrestaShop 1.5'); parent :: __construct(); } public function install() { return parent :: install() && $this->registerHook('leftColumn'); } public function hookDisplayLeftColumn($params) { return $this->display(__FILE__, 'left-column.tpl'); } }
On greffe le module sur le hook leftColumn
lors de l’installation, afin de créer un lien vers la page front office du module.
On note au passage le changement de convention de nommage des hooks : les hooks d’affichage (ceux qui sont visibles), sont désormais préfixés par hookDisplay
, tandis que les hooks d’action sont préfixés par hookAction
.
Création de la vue pour le hook
Les vues pour les hooks sont désormais à placer dans le dossier /views/templates/hook
.
On créé donc le fichier left-column.tpl
<a href="{$link->getModuleLink('prcustomeropinion')}"> {l s='Give my opinion' mod='prcustomeropinion'} </a>
On appelle la méthode getModuleLink
de la classe Link afin de générer un lien vers la page du module.
Les liens seront de la forme :
URLs classique
http://www.monsite.com/index.php?fc=module&module=prcustomeropinion&controller=default
URL SEO
http://www.monsite.com/module/prcustomeropinion/default
Le Modèle
Nous allons gérer le stockage des avis via la table ci-dessous. On stocke une référénce vers le client qui a donné son avis via la colonne id_customer
.
mysql> desc ps_opinion; +-------------+------------------------------------+ | Field | Type | +-------------+------------------------------------+ | id_opinion | int(10) unsigned | | id_customer | int(10) unsigned | | opinion | enum('AVERAGE','GOOD','VERY_GOOD') | | active | tinyint(1) unsigned | +-------------+------------------------------------+
Dans le dossiers /models, on crée un fichier Opinion.php
, pour définir le modèle objet :
class Opinion extends ObjectModel { public $id_opinion; public $id_customer; public $opinion; public $active; public static $definition = array( 'table' => 'opinion', 'primary' => 'id_opinion', 'multilang' => false, 'fields' => array( 'id_opinion' => array( 'type' => ObjectModel :: TYPE_INT ), 'id_customer' => array( 'type' => ObjectModel :: TYPE_INT, 'required' => true ), 'opinion' => array( 'type' => ObjectModel :: TYPE_STRING, 'required' => true ), 'active' => array( 'type' => ObjectModel :: TYPE_BOOL, 'required' => true ) ) ); }
Comme précédemment, la classe doit étendre ObjectModel
afin de pouvoir disposer des fonctionnalités ORM et CRUD, et exposer des propriétés publiques pour les champs de la table.
La nouveauté est l’utilisation du tableau statique $definition
pour spécifier les caractéristiques de la table. Ici, on spécifie simplement les champs obligatoires de l’objet.
On ajoute également une méthode permettant de charger tous les avis utilisateurs validés pour l’affichage :
public static function findAll() { $sql = 'select * from ' . _DB_PREFIX_ . 'opinion where active = 1'; if ($rows = Db :: getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS($sql)) { return ObjectModel :: hydrateCollection(__CLASS__, $rows); } return array(); }
On note l’utilisation de la méthode ObjectModel :: hydrateCollection
, qui permet de transformer un tableau de résultats MySQL en instances de classe.
Création du Controller de front office
Affichage de la liste des avis
Dans le dossier /controllers/front
, nous ajoutons un fichier default.php
, qui va contenir la classe du Controller.
class PrCustomerOpinionDefaultModuleFrontController extends ModuleFrontController { public function initContent() { parent :: initContent(); $opinions = Opinion :: findAll(); $this->context->smarty->assign('opinions', $opinions); $this->setTemplate('form.tpl'); } }
On récupère le tableau d’objets de type Opinion
, et on l’assigne à la vue form.tpl
, à placer dans le dossier /views/templates/front :
<h3>{l s='What people think' mod='prcustomeropinion'}</h3> <table style="width: 100%;"> {foreach from=$opinions item=opinion} <tr> <td>{$opinion->getCustomerName()}</td> <td>{$opinion->opinion}</td> </tr> {/foreach} </table>
Formulaire client
On va maintenant créer un formulaire qui permet aux client de donner leur avis, autrement dit d’ajouter un enregistrement en base de données.
<h2>{l s='Give your opinion' mod='prcustomeropinion'}</h2> <form method="post" class="std"> <fieldset> <label for="opinion" id="opinion">{l s='Our website is...' mod='prcustomeropinion'}</label> <select name="opinion"> <option>--</option> <option value="VERY_GOOD">{l s='Very Good' mod='prcustomeropinion'}</option> <option value="GOOD">{l s='Good' mod='prcustomeropinion'}</option> <option value="AVERAGE">{l s='Average' mod='prcustomeropinion'}</option> </select> <input type="submit" class="button" value="{l s='Give my opinion' mod='prcustomeropinion'}" /> </fieldset> </form>
Dans le Controller, on ajoute le traitement du formulaire (la vérification de l’existence du client a été omise pour l’exemple) :
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($opinion = Tools :: getValue('opinion', false)) { $opinionObj = new Opinion(); $opinionObj->id_customer = $this->context->customer->id; $opinionObj->active = false; $opinionObj->opinion = $opinion; $opinionObj->add(); $link = new Link(); Tools :: redirect($link->getModuleLink('prcustomeropinion', 'default')); } }
On se sert simplement des méthodes CRUD de la classe ObjectModel pour stocker l’objet en base.
Lorsque le formulaire est posté, un enregistrement est ajouté dans la table.
La page du module s’affiche bien dans la colonne centrale du thème.
Création du Controller de back office
Nous allons désormais ajouter un onglet dans le back office permettant de valider les avis client, afin que ceux-ci apparaissent sur le front office.
Nous souhaitons donc afficher une liste d’avis, avec des boutons permettant de valider un avis ou de le supprimer.
La classe du Controller
Dans /controllers/admin
, on créé un fichier adminopinion.php
, qui contrôle le modèle Opinion
. Les Controllers de back office sont en effet prévus par défaut pour gérer un modèle objet. Il est cependant possible de faire ce que l’on veut.
class AdminOpinionController extends ModuleAdminController { public function __construct() { $this->table = 'opinion'; $this->className = 'Opinion'; parent :: __construct(); } }
Attention, les conventions de nommage sont importantes pour garantir le fonctionnement du Controller.
On ajoute ensuite l’onglet :
Affichage de la liste d’avis à valider
Afin de simplifier et homogéniser l’affichage du back office, PrestaShop 1.5 fournit des View Helpers (Aides de vue) : ce sont en quelques mots des classes qui génèrent du code HTML.
Pour afficher de manière simple la liste des avis, il suffit de specifier des propriétés dans le constructeur du module, par exemple :
$this->fields_list = array( 'id_opinion' => array( 'title' => '#' ), 'id_customer' => array( 'title' => $this->module->l('Customer'), 'callback' => 'getCustomerName' ), 'opinion' => array( 'title' => $this->module->l('Opinion') ), 'active' => array( 'title' => $this->module->l('Valider'), 'active' => 'status' ) ); $this->actions = array('delete');
La propriété $fields_list
permet de personnaliser l’affichage des colonnes de la liste.
Dans l’exemple ci-dessus, la colonne active
affichera le bouton standard rouge/vert, et mettra à jour le statut de l’objet en base de données.
Afin de personnaliser la colonne id_customer
et afficher le nom complet du client (au lieu de son identifiant), on utilise une fonction de callback. C’est à dire que pour chaque élément de la liste, PrestaShop va appeler une méthode, qui doit retourner la valeur à afficher. Par défaut, la méthode est appelée sur le Controller
public function getCustomerName($echo, $row) { $id_customer = $row['id_customer']; $customer = new Customer($id_customer); return $customer->firstname . ' ' . $customer->lastname; }
Nous ajoutons également le bouton permettant de supprimer un élément de la liste via la variable $actions
Et… C’est tout !
En effet, dans ce cas il est inutile de créer une quelconque vue, car l’affichage est intégralement pris en charger par les View Helpers.
Pour aller plus loin
Les possibilités offertes par PrestaShop 1.5 en matière de MVC permettent donc de mieux structurer les différents composants back et front office du module, en répartissant les responsabilités au sein de différentes classes.
Le support des Controllers de front office est en très gros plus, et va permettre aux développeurs de personnaliser l’apparence, voire le fonctionnement intégral du front office.
Désormais, les modules sont à concevoir comme des APIs : en effet, chaque Controller de module dispose d’une référence au module, et peut en appeler les méthodes.
Ressources
Si vous souhaitez les étudier, les sources du module sont à disposition sur le GitHub de Prestarocket.