Rajouter un nouveau hook dans Prestashop 1.4 grace à l’override

Prestashop, dans sa dernière version (de mars 2011), propose une vraie amélioration (parmi beaucoup d’autres) : l’override.

Avant de rentrer dans les détails de ce système, rappelons comment est architecturé Prestashop :

  • Les classes, dans /classes, définissent le comportement des fonctionnalités principales (Cart, Order, Customer, etc)
  • Les modules, dans /modules, permettent d’étendre les possibilités de base (newsletter, alertes email, paiement, etc)

Parallèlement, il existe à de nombreux endroits dans Prestashop des hooks (« crochets ») qui sont des points dans le code sur lesquels les modules peuvent se brancher (dans les classes, ou même dans les fichiers à la racine, qui gèrent le panier, le compte-client, etc).

Une bonne manière de comprendre le fonctionnement de ces « hooks » est de les comparer au déroulement d’une vie : vous pouvez intervenir à des moments stratégiques de celle-ci, pour modifier ou non le comportement d’une personne. C’est un peu pareil avec les hooks : ils permettent aux modules de déclencher des actions à des moments-clés du fonctionnement de Prestashop (à l’affichage de l’en-tête, mais aussi au passage d’une commande ou lors de son annulation, etc). Autre exemple : un développeur peut décider d’utiliser le hook « panier » et d’afficher à cet emplacement du contenu (par exemple la météo ou des produits associés).

Certains hooks sont « invisibles » et sont plus des hooks de calcul ou de traitement (génération de PDF, envoi de mail).

Le problème avec ces hooks est qu’ils sont pré-existants dans l’application : en clair, il peut arriver qu’il en manque un précisément là où il serait nécessaire.

Jusqu’à Prestashop 1.3.7, le seul moyen était de le rajouter à la main dans le code, mais avec le risque qu’à la mise à jour suivante, ces modifications disparaîtraient.

Depuis Prestashop 1.4, il existe un dispositif qui permet de résoudre ce problème : l’override.

Derrière cet anglicisme se cache tout simplement la possibilité de « surcharger » Prestashop, c’est à dire de redéfinir ce que fait l’application, et sans craindre de perdre ces changements à la prochaine évolution.

En effet, ces « surcharges » se situent dans un répertoire /override, vide par défaut dans les versions téléchargeables de l’outil, donc qui ne sera pas écrasé par une mise à jour.


Prenons un exemple pratique, pour voir comment tout cela se décompose.

Pitch : On désire rajouter du contenu avant le footer, dans une zone qu’on appellera « surfooter », indépendante du bas de page.

– 1ère étape : Créer le hook dans la base de données

En effet, pour que Prestashop puisse déclencher une action sur un hook, il faut que celui-ci soit référencé dans la liste des choix possibles.

On va ici utiliser PHPMyAdmin pour l’insérer. Rendez-vous dans la table ps_hook (ou toute table finissant par « _hook », selon le préfixe que vous avez utilisé). Cliquez sur Insérer, et complétez comme suit l’écran qui apparaît :

Et cliquez sur Exécuter.

Pour les fans du SQL, la commande suivante est équivalente :

INSERT INTO ps_hook (name,title,position,live_edit) VALUES ('surfooter', 'Hook surFooter', '1', '0')

– 2ème étape : Positionner le hook dans le code de l’application

Dans l’exemple qu’on a choisi, le « surfooter » doit se déclencher juste avant le « footer ». Comment se déclenche le hook « footer » ? Regardons dans le fichier footer.php (logique, non?) :

$controller = new FrontController();
$controller->displayFooter();

Depuis la 1.4, on utilise dans Prestashop des contrôleurs, réunis dans un dossier /controllers, qui contiennent les fonctions utilisées dans les différentes pages.

Regardons le code de la fonction displayFooter dans /classes/FrontController.php :

public function displayFooter()
	{
		global $cookie;
		if (!self::$initialized)
			$this->init();

		self::$smarty->assign(array(
			'HOOK_RIGHT_COLUMN' => Module::hookExec('rightColumn', array('cart' => self::$cart)),
			'HOOK_FOOTER' => Module::hookExec('footer'),
			'content_only' => (int)(Tools::getValue('content_only'))));
		self::$smarty->display(_PS_THEME_DIR_.'footer.tpl');
		//live edit
		if (Tools::isSubmit('live_edit') AND $ad = Tools::getValue('ad') AND (Tools::getValue('liveToken') == sha1(Tools::getValue('ad')._COOKIE_KEY_)))
		{
			self::$smarty->assign(array('ad' => $ad, 'live_edit' => true));
			self::$smarty->display(_PS_ALL_THEMES_DIR_.'live_edit.tpl');
		}
		else
			Tools::displayError();
	}

La ligne HOOK_FOOTER’ => Module::hookExec(‘footer’) permet de déclencher « les fonctions hookfooter situés dans les modules installés sur la boutique ». Parallèlement, on retrouve dans le template un bloc {$HOOK_FOOTER} qui affichera là ou il est inséré le contenu généré par les fonctions qu’on vient évoquer.

Pour notre « surfooter », il suffirait donc de rajouter :

HOOK_SURFOOTER' => Module::hookExec('surfooter'),

Mais (il y a un « mais »), comme on l’a précisé au début de l’article, ces fichiers du coeur de Prestashop seront écrasés à la prochaine mise à jour. On va donc « dupliquer » cette fonction, et créer un fichier /override/classes/FrontController.php, contenant :

//La classe d'origine située dans /classes/FrontController.php s'appelle FrontControllerCore, on utilise donc ici FrontController
class FrontController extends FrontControllerCore
{
	//Copie de la fonction displayFooter située dans /classes/FrontController.php, mais avec le "surfooter" en +
	//C'est la copie qui prendra la main sur la fonction d'origine appelée dans /footer.php
    public function displayFooter()
	{
		global $cookie;
		if (!self::$initialized)
			$this->init();

		self::$smarty->assign(array(
			'HOOK_RIGHT_COLUMN' => Module::hookExec('rightColumn', array('cart' => self::$cart)),
			//On rajoute l'appel à notre nouveau hook
			'HOOK_SURFOOTER' => Module::hookExec('surfooter'),
			'HOOK_FOOTER' => Module::hookExec('footer'),
			'content_only' => (int)(Tools::getValue('content_only'))));
		self::$smarty->display(_PS_THEME_DIR_.'footer.tpl');
		//live edit
		if (Tools::isSubmit('live_edit') AND $ad = Tools::getValue('ad') AND (Tools::getValue('liveToken') == sha1(Tools::getValue('ad')._COOKIE_KEY_)))
		{
			self::$smarty->assign(array('ad' => $ad, 'live_edit' => true));
			self::$smarty->display(_PS_ALL_THEMES_DIR_.'live_edit.tpl');
		}
		else
			Tools::displayError();
	}
}

– 3ème étape : « greffer » un module sur notre hook

Que l’on souhaite être directement connecté au « surfooter », ou qu’on souhaite les relier ensuite par « Greffer un module », la procédure est la même : il faut déclarer au début du fichier du module que l’on souhaite s’y connecter.

Prenons l’exemple de « Bloc newsletter », (/modules/blocknewsletter/blocknewsletter.php) et de sa fonction install() (présente dans toutes les extensions Prestashop) :

if (parent::install() == false OR $this->registerHook('leftColumn') == false OR $this->registerHook('header') == false)
	return false;

On voit qu’est utilisée plusieurs fois la fonction registerHook, qui permet de se « référencer » sur un hook précis. On va donc modifier cette ligne ainsi :

if (parent::install() == false OR $this->registerHook('leftColumn') == false
OR $this->registerHook('header') == false  OR $this->registerHook('surfooter') == false) {
	return false;
}

Désormais, à la prochaine installation de ce module (donc par exemple si on le désinstalle/réinstalle), il se branchera sur « surfooter » et cherchera à éxécuter sa propre fonction hooksurfooter(), qu’on va créer pour l’occasion (toujours dans le même fichier /modules/blocknewsletter/blocknewsletter.php) :

function hooksurfooter($params)
	{
		//Mettre ici du code à exécuter dans le surfooter
	}

Il faudra bien sur rajouter le bout de code nécessaire dans notre template, par exemple:

{$HOOK_SURFOOTER}

(merci à Jérémy Kleinclaus pour la remarque à ce sujet là)
Bien sur, comme toute modification importante sur un template et sur la boutique, il peut être nécessaire de vider le cache Prestashop si les changements n’apparaissent pas.

On peut également utiliser ce procédé pour modifier le fonctionnement d’une fonction du core de Prestashop, sans nécessairement vouloir rajouter un hook, ou « surcharger » un contrôleur, en utilisant le répertoire /override/controllers.

26 réponses sur “Rajouter un nouveau hook dans Prestashop 1.4 grace à l’override”

  1. Bravo ! article bien rédigé et qui devrait donner un bon coup de pouce à beaucoup de développeurs 😉

    A quand une traduction en anglais ? n’hésite pas à demander de l’aide aux modérateurs anglophones du forum, je pense que ça leur fera très plaisir !

  2. Hello,

    Une simple question : pourquoi recenser les hooks en base au lieu d’un fichier XML, ou autre moyen plus « code » ? C’est un peu bizarre.

  3. Plusieurs raisons :
    – Pouvoir les lister et les modifier plus facilement
    – Cela évite des accès disque inutiles

    Quel avantage à les avoir sous forme XML ?

  4. Bonjour,
    bel article effectivement, mais une remarque/question quand-même. Une fois que j’ai suchargé mon controller pour créer un nouveau hook, je dois aller modifier le code d’un module pour l’afficher dans ce hook et là à la prochaine mise à jour la modification ne sera plus active et je dois remodifier le code du module. Est-il possible de surcharger aussi le module ? sinon quel intérêt de la surcharge pour les nouveaux hooks ?

  5. Cette surcharge a un intérêt pour les modules qui utiliseront justement ces nouveaux hooks (si vous modifiez un module existant, le problème reste le même qu’avec les classes du core de Prestashop).

  6. Pouvoir les modifier plus facilement, en base ? Càd devoir faire des requêtes SQL plutôt que pouvoir les modifier sous Vi et Eclipse ?

    C’est qd même très discutable comme choix. Sans parler du versioning, qui va impliquer de faire des fichiers .sql à chaque hook et à les versionner, ils vont donc devoir être ds un coin à part ds un repository…

    J’ai plus l’habitude de voir ça dans du fichier pur, sur une dizaine de solutions opensource déployées en entreprise que j’ai pu croiser…

  7. Vince : remettons les choses à leur place, c’est une modification qui intervient rarement ce n’est pas non plus un ajout d’utilisateur ou une opération équivalente.

    L’ajout de hooks reste une opération exceptionnelle : en effet, la mise en place de la forge Prestashop permet (dans la mesure du possible) aux développeurs de proposer des nouveaux hooks pour la prochaine release.

    Dans beaucoup de solutions OpenSource il n’existe même pas de liste de hooks (ici c’est la notion de position des modules sur chaque hook qui impose l’utilisation d’une table et donc d’IDs pour chaque hook)

  8. Bonjour,

    vous avez oublié de préciser qu’il faut à un moment donné préciser où doit se situer le nouveau HOOK dans le template.
    Donc modifier footer.tpl et y ajouter à l’endroit désiré {$HOOK_SURFOOTER}

    1. En fait, c’est précisé dans l’article : »Parallèlement, on retrouve dans le template un bloc {$HOOK_FOOTER} qui affichera là ou il est inséré le contenu généré par les fonctions qu’on vient évoquer. » mais je l’avoue, ce n’est pas très clair, je vais reprendre un exemple en fin de page.

  9. Oui je l’ai vu par la suite 🙂 Mais étant donné que c’était ma première création de Hook et que je suis passé à côté de cette info, j’imagine que d’autres novices auraient également pu louper ça et perdre quelques heures à comprendre pourquoi cela ne s’affiche pas 🙂

    Donc merci pour la modif !

    Bonne continuation,
    Jérémy

  10. Hello,

    Peut on passer par un module pour créer des nouveaux hook ?
    Le module se chargerai d’ajouter dans la bdd les nouveaux hook. Par contre, comment faire pour le positionner via un module ?
    ++

    1. L’ajout du hook dans la base n’est pas réellement un souci, un module peut rajouter facilement une entrée dans ps_hook.

      C’est le positionnement du hook qui, lui, pose souci : tu es obligé de passer par l’override (et donc de copier manuellement ton fichier d’override dans le bon dossier)

        1. Pour des hooks pouvant se brancher sur des hooks existants, oui, pourquoi pas.

          Par contre, dans certains cas, tu veux rajouter des hooks justement là ou il n’y en a pas 🙂 (et là, il faut utiliser l’override des controleurs)

          1. Si c’est uniquement pour de l’affichage, c’est donc une bonne méthode ?

          2. Question bête : tu ne peux pas te brancher sur un hook existant ? tu peux m’en dire plus sur le hook qui tintéresse ?

          3. Je souhaite créer un hook pour un menu horizontal et un autre hook dans le header de la boutique. Actuellement, il n’y a qu’un seul hook : Top et c’est pas pratique pour la création de thèmes.
            En gros je souhaite avoir ça :

            {HOOK_TOP}
            {HOOK_TOPBOTTOM}

            {HOOK_NAV}

          4. Justement, hookTop te bloque à quel niveau? (loin de moi l’idée de t’empecher de créer un nouveau hook, mais si on peut éviter c’est mieux 🙂

  11. Merci pour cet article !

    Une petite correction : j’ai trouvé le fichier FrontController.php dans /classes et non /controller comme indiqué à la 5ème ligne de l’étape 2. C’est par contre le bon dans les commentaires du code et dans la suite de l’article mais ça m’a fait chercher un bon moment !

Laisser un commentaire

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