Passer une variable Smarty dans jQuery sur PrestaShop
Comment récupérer une variable Smarty dans un script jQuery sur PrestaShop : attributs data, balise literal, boucles foreach et appels AJAX.
En bref : Pour récupérer une variable Smarty dans jQuery au sein d'une boucle foreach, utilisez les attributs HTML5 data-* pour injecter les valeurs dans le DOM, la balise {literal} pour protéger le JavaScript inline, et $(this).data('key') pour cibler l'élément précis cliqué par l'utilisateur.
Le problème : Smarty et JavaScript ne parlent pas le même langage
Quand on développe un module ou un thème PrestaShop, on se retrouve tôt ou tard face à ce mur : comment récupérer une variable Smarty (côté serveur) dans un script jQuery (côté client) ?
La tentation naturelle est d'écrire quelque chose comme :
var idmsg = $(".test").val();
Mais Voici la bonne approche, propre et maintenable. La technique repose sur deux mécanismes complémentaires : Smarty utilise les accolades PrestaShop 1.7+ et 8.x : Si vous utilisez les fichiers Dans votre fichier Points importants : Le secret est dans Pour bien comprendre le piège, détaillons ce qui se passe avec les approches naïves : ` Sur les versions modernes de PrestaShop, la bonne pratique est de séparer complètement le JavaScript du template Smarty. Cette architecture est plus propre, plus sécurisée (token CSRF, contrôleur dédié) et plus facile à déboguer. Même si la valeur vient d'un attribut Si vos éléments sont générés dynamiquement (AJAX, pagination), Tout ce que vous devez savoir sur ce sujet. Un projet PrestaShop ? Discutons-en directement. 193 projets livrés Alexandre Carette Expert PrestaShop & Architecture E-commerce Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants. .val() ne fonctionne que sur les éléments de formulaire (input, select, textarea). Sur un ou un undefined. Et même en utilisant .text() ou .html(), le vrai piège arrive quand on travaille dans une boucle {foreach} : le sélecteur de classe cible tous les éléments portant cette classe, et jQuery ne récupère que la valeur du premier (ou du dernier, selon la méthode).
Solution : les attributs HTML5 `data-*` combinés à `{literal}`
Pourquoi `{literal}` est indispensable
{} comme délimiteurs. Or jQuery et JavaScript en font un usage intensif (objets, fonctions, callbacks). Sans {literal}, Smarty tente d'interpréter chaque { de votre code JavaScript et provoque des erreurs de compilation du template.
{literal}
<script type="text/javascript">
// Ici, les accolades sont protégées de Smarty
jQuery(document).ready(function() {
// Votre code JS en toute sécurité
});
</script>
{/literal}
.js séparés (bonne pratique), le problème {literal} ne se pose plus puisque Smarty ne traite pas les fichiers JavaScript externes. Privilégiez cette approche dans vos modules modernes.Le template : injecter les données dans le DOM
.tpl, utilisez les attributs data-* pour attacher chaque valeur Smarty à son élément HTML :
{foreach from=$messages item=message name=messageLoop}
<div class="message-item" data-id="{$message.id_msg|intval}">
<div class="message-content">
{$message.content|escape:'html':'UTF-8'}
</div>
<button class="btn-mark-read" data-id="{$message.id_msg|intval}">
Marquer comme lu
</button>
</div>
{/foreach}
Le JavaScript : récupérer la bonne valeur au clic
{literal}
<script type="text/javascript">
jQuery(document).ready(function($) {
$('.btn-mark-read').on('click', function(e) {
e.preventDefault();
// $(this) cible le bouton cliqué, pas tous les boutons
var idMsg = $(this).attr('data-id');
// Ou en syntaxe moderne : $(this).data('id');
$.ajax({
url: '{/literal}{$module_dir}{literal}ajax/mark-read.php',
type: 'POST',
data: {
id_msg: idMsg,
token: '{/literal}{$token}{literal}'
},
dataType: 'json',
success: function(response) {
if (response.success) {
// Retirer visuellement le message ou changer son style
$(e.target).closest('.message-item').addClass('read');
}
},
error: function(xhr, status, error) {
console.error('Erreur AJAX:', error);
}
});
});
});
</script>
{/literal}
$(this) : à l'intérieur du gestionnaire d'événement click, this référence l'élément précis qui a été cliqué, pas l'ensemble des éléments correspondant au sélecteur. C'est ce qui résout le problème du foreach.Pourquoi `.val()` et `.text()` échouent dans une boucle
Méthode Comportement dans un foreach Résultat `$('.test').val()` Cible le premier élément `.test`, renvoie `undefined` sur un ` Échec total `$('.test').text()` Cible le premier élément `.test` uniquement Toujours la même valeur `$('.test').each()` Parcourt tous les éléments mais sans contexte de clic Pas de lien avec l'interaction utilisateur `$(this).data('id')` Cible l'élément cliqué précisément Valeur correcte à chaque clic Approche moderne : PrestaShop 1.7+ et 8.x
1. Enregistrer le script dans votre module
public function hookDisplayHeader()
{
// Passer les données PHP → JS via Media::addJsDef
Media::addJsDef([
'myModuleAjaxUrl' => $this->context->link->getModuleLink(
$this->name,
'ajax',
[],
true
),
'myModuleToken' => Tools::getToken(false)
]);
$this->context->controller->addJS(
$this->_path . 'views/js/front.js'
);
}
2. Le fichier JavaScript externe
// views/js/front.js
// Pas besoin de {literal} puisque Smarty ne traite pas ce fichier
jQuery(document).ready(function($) {
$(document).on('click', '.btn-mark-read', function(e) {
e.preventDefault();
var idMsg = $(this).data('id');
$.ajax({
url: myModuleAjaxUrl,
type: 'POST',
data: {
action: 'markAsRead',
id_msg: idMsg,
token: myModuleToken
},
dataType: 'json',
success: function(response) {
if (response.success) {
$(e.target).closest('.message-item')
.fadeOut(300, function() {
$(this).addClass('read').fadeIn(300);
});
}
}
});
});
});
3. Le contrôleur AJAX côté module
// controllers/front/ajax.php
class MyModuleAjaxModuleFrontController extends ModuleFrontController
{
public function initContent()
{
if (!Tools::getIsset('token')
|| Tools::getValue('token') !== Tools::getToken(false)) {
die(json_encode(['success' => false, 'error' => 'Invalid token']));
}
$action = Tools::getValue('action');
if ($action === 'markAsRead') {
$idMsg = (int) Tools::getValue('id_msg');
// Votre logique métier ici
$result = Db::getInstance()->update(
'my_messages',
['is_read' => 1],
'id_msg = ' . $idMsg
);
die(json_encode(['success' => (bool) $result]));
}
}
}
Les erreurs classiques à éviter
1. Oublier d'échapper les variables Smarty
{* DANGEREUX — injection XSS possible *}
<div data-name="{$message.name}">
{* CORRECT *}
<div data-name="{$message.name|escape:'html':'UTF-8'}">
2. Construire la query string manuellement
// FRAGILE — pas d'encodage, pas de structure
var dataString = '&idmsg=' + idmsg;
// CORRECT — jQuery encode automatiquement
$.ajax({
data: { id_msg: idmsg }
});
3. Ne pas valider côté serveur
data-* que vous avez généré, validez toujours côté PHP :
$idMsg = (int) Tools::getValue('id_msg');
if ($idMsg <= 0) {
die(json_encode(['success' => false, 'error' => 'ID invalide']));
}
4. Utiliser `.click()` au lieu de `.on('click')`
.click() ne fonctionnera pas sur les nouveaux éléments. Utilisez la délégation d'événements :
// Ne fonctionne PAS sur les éléments ajoutés après le chargement
$('.btn-mark-read').click(function() { ... });
// Fonctionne TOUJOURS grâce à la délégation
$(document).on('click', '.btn-mark-read', function() { ... });
Récapitulatif
Contexte Technique recommandée PrestaShop 1.6 avec JS inline `{literal}` + attributs `data-*` + `$(this)` PrestaShop 1.7 / 8.x `Media::addJsDef()` + fichier JS externe + contrôleur AJAX Boucle foreach Toujours `$(this).data('key')`, jamais sélecteur de classe global Sécurité Token CSRF + `intval` + `escape` + validation serveur Questions fréquentes
Lire sur le blog
