⚡ AJAX en modulos PrestaShop
Actualizado: 2024-12-01
Los modulos de PrestaShop exponen endpoints AJAX creando un ModuleFrontController con la propiedad $ajax = true. Las llamadas AJAX deben incluir un token de seguridad para evitar CSRF y llamadas no autorizadas.
#Controlador AJAX con ModuleFrontController
controllers/front/ajax.php — endpoint AJAX con acciones
php
<?php
if (!defined('_PS_VERSION_')) { exit; }
class MyModuleAjaxModuleFrontController extends ModuleFrontController
{
/** @var bool Solo responde JSON, sin layout HTML */
public $ajax = true;
public function initContent(): void
{
parent::initContent();
$action = Tools::getValue('action', '');
header('Content-Type: application/json; charset=utf-8');
switch ($action) {
case 'getItems':
$this->processGetItems();
break;
case 'saveItem':
$this->processSaveItem();
break;
case 'deleteItem':
$this->processDeleteItem();
break;
default:
$this->ajaxRender(json_encode([
'success' => false,
'error' => 'Accion no valida: ' . htmlspecialchars($action),
]));
}
}
private function processGetItems(): void
{
$items = Db::getInstance()->executeS(
'SELECT * FROM `' . _DB_PREFIX_ . 'mymodule_items`
WHERE active = 1 ORDER BY sort_order ASC'
);
$this->ajaxRender(json_encode([
'success' => true,
'items' => $items ?: [],
]));
}
private function processSaveItem(): void
{
// Validar token CSRF
$token = Tools::getValue('token');
if (!$token || $token !== Tools::getToken(false)) {
$this->ajaxRender(json_encode(['success'=>false,'error'=>'Token invalido']));
return;
}
$name = trim(Tools::getValue('name', ''));
if (empty($name)) {
$this->ajaxRender(json_encode(['success'=>false,'error'=>'Nombre requerido']));
return;
}
Db::getInstance()->insert('mymodule_items', [
'name' => pSQL($name),
'active' => 1,
]);
$this->ajaxRender(json_encode([
'success' => true,
'id' => (int) Db::getInstance()->Insert_ID(),
]));
}
private function processDeleteItem(): void
{
$id = (int) Tools::getValue('id');
if ($id <= 0) {
$this->ajaxRender(json_encode(['success'=>false,'error'=>'ID invalido']));
return;
}
Db::getInstance()->delete('mymodule_items', 'id_mymodule_item = ' . $id);
$this->ajaxRender(json_encode(['success' => true]));
}
}
#Seguridad: validacion de token
Generar y validar tokens anti-CSRF
php
<?php
// ── GENERAR TOKEN (en el template que inicia la llamada AJAX) ──
// En el FrontController que muestra la pagina:
$this->context->smarty->assign([
'ajax_token' => Tools::getToken(false), // token estatico de sesion
'ajax_url' => $this->context->link->getModuleLink('mymodule', 'ajax'),
]);
// En el template Smarty:
// {assign var='token' value={Tools::getToken(false)}}
// ── VALIDAR TOKEN (en el controlador AJAX) ──
$token = Tools::getValue('token');
if (!$token || $token !== Tools::getToken(false)) {
http_response_code(403);
$this->ajaxRender(json_encode(['error' => 'Forbidden']));
return;
}
// ── ALTERNATIVA: static_token (no cambia entre peticiones de la misma sesion) ──
// Generar:
$staticToken = Tools::encrypt('mymodule-ajax-secret');
// Validar:
if (Tools::getValue('static_token') !== Tools::encrypt('mymodule-ajax-secret')) {
// token invalido
}
#JavaScript: fetch API (moderno)
Llamadas AJAX con fetch (ES6+)
javascript
// Asumiendo que PHP exporto ajaxUrl y ajaxToken al JS
// (ver seccion 'Exportar URL al JS')
// GET request
async function getItems() {
try {
const resp = await fetch(window.MyModule.ajaxUrl + '?action=getItems');
const data = await resp.json();
if (data.success) {
console.log('Items:', data.items);
}
} catch (err) {
console.error('Error AJAX:', err);
}
}
// POST request con token
async function saveItem(name) {
const formData = new FormData();
formData.append('action', 'saveItem');
formData.append('name', name);
formData.append('token', window.MyModule.token);
try {
const resp = await fetch(window.MyModule.ajaxUrl, {
method: 'POST',
body: formData,
});
const data = await resp.json();
if (data.success) {
console.log('Guardado con ID:', data.id);
} else {
alert('Error: ' + data.error);
}
} catch (err) {
console.error('Error:', err);
}
}
#JavaScript: jQuery AJAX (legacy)
Llamadas AJAX con jQuery (compatible con PS 1.7)
javascript
// GET
$.ajax({
url: window.MyModule.ajaxUrl,
data: { action: 'getItems' },
dataType: 'json',
success: function(data) {
if (data.success) {
console.log(data.items);
}
},
error: function(xhr, status, error) {
console.error('Error AJAX:', error);
}
});
// POST con token
$.ajax({
url: window.MyModule.ajaxUrl,
method: 'POST',
data: {
action: 'saveItem',
name: $('#item-name').val(),
token: window.MyModule.token
},
dataType: 'json',
success: function(data) {
if (data.success) {
console.log('ID:', data.id);
}
}
});
#Exportar URL al JS desde PHP
Exportar variables PHP al JavaScript via hookActionFrontControllerSetMedia
php
<?php
// En el modulo — usar el hook de media para añadir JS inline con las URLs
public function hookActionFrontControllerSetMedia(): void
{
// Solo en la pagina del modulo
$controller = $this->context->controller;
if (!($controller instanceof ModuleFrontController) || $controller->module->name !== $this->name) {
return;
}
$jsVars = [
'ajaxUrl' => $this->context->link->getModuleLink($this->name, 'ajax', [], true),
'token' => Tools::getToken(false),
'baseUrl' => $this->context->link->getBaseLink(),
];
$jsInline = 'window.MyModule = ' . json_encode($jsVars) . ';';
$this->context->controller->registerJavascript(
$this->name . '-vars',
'data:text/javascript,' . rawurlencode($jsInline),
['server' => 'remote', 'position' => 'head', 'priority' => 1]
);
}
Descargar en Markdown
Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.