⬆️ Upgrade scripts — actualizar modulo sin perder datos
Actualizado: 2025-01-15
#Como funciona el upgrade en PS
Cuando subes una nueva version de tu modulo, PrestaShop compara la version instalada (en BD) con la version del archivo .php principal. Si difieren, ejecuta automaticamente todos los scripts de upgrade/ cuya version sea mayor que la instalada y menor o igual que la nueva.
Orden de ejecucion
text
Version instalada: 1.0.0
Version nueva: 1.3.0
PS ejecuta en orden:
upgrade/upgrade-1.1.0.php ✓
upgrade/upgrade-1.2.0.php ✓
upgrade/upgrade-1.2.5.php ✓
upgrade/upgrade-1.3.0.php ✓
NO ejecuta:
upgrade/upgrade-1.0.0.php ✗ (version actual o menor)
upgrade/upgrade-1.4.0.php ✗ (mayor que la nueva version)
#Estructura de scripts de upgrade
Arbol de archivos
text
modules/ecom_mimodulo/
├── ecom_mimodulo.php # version = '1.3.0'
└── upgrade/
├── upgrade-1.1.0.php # Anadir tabla nueva
├── upgrade-1.2.0.php # Anadir columna + migrar datos
├── upgrade-1.2.5.php # Registrar nuevo hook
└── upgrade-1.3.0.php # Renombrar config keys
Formato basico de un script de upgrade
php
<?php
/**
* upgrade/upgrade-1.1.0.php
*
* Naming OBLIGATORIO: upgrade-{version}.php
* Funcion OBLIGATORIA: upgrade_module_{version_con_underscores}($module)
*/
if (!defined('_PS_VERSION_')) {
exit;
}
function upgrade_module_1_1_0($module)
{
// $module es la instancia de tu modulo
// Devolver true = exito, false = error (PS aborta el upgrade)
// ... hacer cambios ...
return true;
}
#Ejemplo: anadir columna a tabla
upgrade/upgrade-1.1.0.php — ALTER TABLE seguro
php
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
function upgrade_module_1_1_0($module)
{
$db = Db::getInstance();
$prefix = _DB_PREFIX_;
// 1. Verificar si la columna ya existe (idempotencia)
$columns = $db->executeS(
"SHOW COLUMNS FROM `{$prefix}ecom_mimodulo` LIKE 'priority'"
);
if (empty($columns)) {
$result = $db->execute(
"ALTER TABLE `{$prefix}ecom_mimodulo`
ADD COLUMN `priority` INT(10) UNSIGNED NOT NULL DEFAULT 0
AFTER `position`"
);
if (!$result) {
return false;
}
}
// 2. Anadir indice si no existe
$indexes = $db->executeS(
"SHOW INDEX FROM `{$prefix}ecom_mimodulo` WHERE Key_name = 'idx_priority'"
);
if (empty($indexes)) {
$db->execute(
"ALTER TABLE `{$prefix}ecom_mimodulo`
ADD INDEX `idx_priority` (`priority`)"
);
}
// 3. Crear tabla nueva si es necesario
$db->execute(
"CREATE TABLE IF NOT EXISTS `{$prefix}ecom_mimodulo_log` (
`id_log` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_mimodulo` INT(10) UNSIGNED NOT NULL,
`action` VARCHAR(50) NOT NULL,
`date_add` DATETIME NOT NULL,
PRIMARY KEY (`id_log`),
KEY `id_mimodulo` (`id_mimodulo`)
) ENGINE=" . _MYSQL_ENGINE_ . " DEFAULT CHARSET=utf8mb4"
);
return true;
}
#Ejemplo: migrar datos
upgrade/upgrade-1.2.0.php — migrar datos entre estructuras
php
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
function upgrade_module_1_2_0($module)
{
$db = Db::getInstance();
$prefix = _DB_PREFIX_;
// Escenario: migrar de config serializada a tabla propia
// Antes: Configuration::get('ECOM_ITEMS') = serialized array
// Ahora: tabla ecom_mimodulo_items
// 1. Crear tabla nueva
$db->execute(
"CREATE TABLE IF NOT EXISTS `{$prefix}ecom_mimodulo_items` (
`id_item` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`value` TEXT,
`position` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`active` TINYINT(1) NOT NULL DEFAULT 1,
`date_add` DATETIME NOT NULL,
PRIMARY KEY (`id_item`)
) ENGINE=" . _MYSQL_ENGINE_ . " DEFAULT CHARSET=utf8mb4"
);
// 2. Migrar datos de Configuration a tabla
$oldData = Configuration::get('ECOM_ITEMS');
if ($oldData) {
$items = json_decode($oldData, true);
if (!$items) {
// Intentar unserialize (formato antiguo)
$items = @unserialize($oldData);
}
if (is_array($items)) {
foreach ($items as $i => $item) {
$db->insert('ecom_mimodulo_items', [
'name' => pSQL($item['name'] ?? 'Item ' . $i),
'value' => pSQL($item['value'] ?? ''),
'position' => (int) $i,
'active' => 1,
'date_add' => date('Y-m-d H:i:s'),
]);
}
}
// 3. Eliminar config vieja
Configuration::deleteByName('ECOM_ITEMS');
}
// 4. Renombrar config keys para consistencia
$renames = [
'ECOM_OLD_KEY_1' => 'ECOM_MIMODULO_ENABLED',
'ECOM_OLD_KEY_2' => 'ECOM_MIMODULO_TITLE',
];
foreach ($renames as $oldKey => $newKey) {
$value = Configuration::get($oldKey);
if ($value !== false) {
Configuration::updateValue($newKey, $value);
Configuration::deleteByName($oldKey);
}
}
return true;
}
#Ejemplo: registrar nuevos hooks
upgrade/upgrade-1.2.5.php — registrar hooks nuevos
php
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
function upgrade_module_1_2_5($module)
{
// Registrar hooks que no existian en versiones anteriores
$newHooks = [
'displayProductExtraContent',
'actionCartSave',
'displayBackOfficeHeader',
];
foreach ($newHooks as $hook) {
if (!$module->isRegisteredInHook($hook)) {
if (!$module->registerHook($hook)) {
// No abortar por un hook — log y continuar
PrestaShopLogger::addLog(
'ecom_mimodulo: Failed to register hook ' . $hook,
2 // Warning
);
}
}
}
// Desregistrar hooks obsoletos
$oldHooks = ['displayHeader']; // Ya no lo necesitamos
foreach ($oldHooks as $hook) {
$module->unregisterHook($hook);
}
// Actualizar valor de configuracion
if (Configuration::get('ECOM_MIMODULO_VERSION') === false) {
Configuration::updateValue('ECOM_MIMODULO_VERSION', $module->version);
}
return true;
}
#Buenas practicas
- SIEMPRE verificar si el cambio ya existe antes de aplicarlo (idempotencia)
- Nunca usar DROP TABLE — solo ALTER TABLE ADD/MODIFY
- Hacer backup de datos antes de migrar (SELECT INTO temporales)
- Devolver false SOLO si el error es critico y el modulo no puede funcionar
- Usar PrestaShopLogger para registrar warnings no criticos
- Testear upgrade desde CADA version anterior soportada
- Incluir en el nombre de funcion la version con underscores exactos
- No acceder a $this — usar el parametro $module
- No asumir que el modulo esta activo durante el upgrade
#Rollback manual
Script de rollback (ejecutar manualmente si falla)
php
<?php
/**
* NO es parte del sistema de upgrade de PS.
* Es un script auxiliar para revertir cambios si algo sale mal.
* Ejecutar via CLI: php modules/ecom_mimodulo/upgrade/rollback-1.2.0.php
*/
require_once dirname(__DIR__, 3) . '/config/config.inc.php';
$db = Db::getInstance();
$prefix = _DB_PREFIX_;
echo "Rollback 1.2.0 — Revertir migracion de datos\n";
// 1. Restaurar Configuration desde tabla
$items = $db->executeS("SELECT * FROM `{$prefix}ecom_mimodulo_items` ORDER BY position");
if ($items) {
Configuration::updateValue('ECOM_ITEMS', json_encode($items));
echo " Config ECOM_ITEMS restaurada con " . count($items) . " items\n";
}
// 2. Restaurar keys renombradas
Configuration::updateValue('ECOM_OLD_KEY_1', Configuration::get('ECOM_MIMODULO_ENABLED'));
Configuration::updateValue('ECOM_OLD_KEY_2', Configuration::get('ECOM_MIMODULO_TITLE'));
Configuration::deleteByName('ECOM_MIMODULO_ENABLED');
Configuration::deleteByName('ECOM_MIMODULO_TITLE');
// 3. Forzar version en BD a la anterior
$db->execute(
"UPDATE `{$prefix}module` SET version = '1.1.0'
WHERE name = 'ecom_mimodulo'"
);
echo "Rollback completado. Version en BD: 1.1.0\n";
Descargar en Markdown
Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.