---
title: Tablas personalizadas en modulos
section: database
slug: custom-tables
description: "Como crear y gestionar tablas personalizadas en modulos PrestaShop: naming conventions, SQL en install(), uninstall(), ObjectModel y upgrades."
keywords: prestashop tabla personalizada modulo CREATE TABLE install uninstall naming ObjectModel upgrade SQL
last_updated: 2024-12-01
source_url: "https://ayudaprestashop.es/database/custom-tables"
---

# Tablas personalizadas en modulos

> Como crear y gestionar tablas personalizadas en modulos PrestaShop: naming conventions, SQL en install(), uninstall(), ObjectModel y upgrades.

Los modulos que necesitan persistir datos propios deben crear sus propias tablas. La convencion de nombres y la estructura de install/uninstall son criticas para la correcta instalacion y limpieza del modulo.

## Convencion de nombres

| Tipo de tabla | Patron de nombre | Ejemplo |
| --- | --- | --- |
| Tabla principal | ps_{modulename}_{entity} | ps_mymodule_item |
| Tabla multiidioma | ps_{modulename}_{entity}_lang | ps_mymodule_item_lang |
| Tabla multitienda | ps_{modulename}_{entity}_shop | ps_mymodule_item_shop |
| Tabla de relacion | ps_{modulename}_{entity1}_{entity2} | ps_mymodule_item_category |
| Tabla de configuracion | ps_{modulename}_config | ps_mymodule_config |
| Tabla de log | ps_{modulename}_log | ps_mymodule_log |

## Crear tablas en install()

*install() con creacion de tablas SQL*

```php
<?php

public function install(): bool
{
    return parent::install()
        && $this->registerHook('displayHome')
        && $this->createTables();
}

private function createTables(): bool
{
    $sql = [];

    // ── Tabla principal ──
    $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mymodule_item` (
        `id_mymodule_item` int(10) unsigned NOT NULL AUTO_INCREMENT,
        `id_category` int(10) unsigned NOT NULL DEFAULT 0,
        `reference` varchar(64) NOT NULL DEFAULT \'\',
        `price` decimal(20,6) unsigned NOT NULL DEFAULT \'0.000000\',
        `active` tinyint(1) unsigned NOT NULL DEFAULT 1,
        `sort_order` int(10) unsigned NOT NULL DEFAULT 0,
        `date_add` datetime NOT NULL,
        `date_upd` datetime NOT NULL,
        PRIMARY KEY (`id_mymodule_item`),
        KEY `id_category` (`id_category`),
        KEY `active` (`active`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;';

    // ── Tabla multiidioma ──
    $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mymodule_item_lang` (
        `id_mymodule_item` int(10) unsigned NOT NULL,
        `id_lang` int(10) unsigned NOT NULL,
        `id_shop` int(10) unsigned NOT NULL DEFAULT 1,
        `name` varchar(255) NOT NULL,
        `description` text,
        PRIMARY KEY (`id_mymodule_item`, `id_lang`, `id_shop`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;';

    // ── Tabla de relaciones (N:N) ──
    $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mymodule_item_category` (
        `id_mymodule_item` int(10) unsigned NOT NULL,
        `id_category` int(10) unsigned NOT NULL,
        PRIMARY KEY (`id_mymodule_item`, `id_category`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;';

    foreach ($sql as $query) {
        if (!Db::getInstance()->execute($query)) {
            return false;
        }
    }

    return true;
}
```

## Eliminar tablas en uninstall()

*uninstall() con DROP TABLE*

```php
<?php

public function uninstall(): bool
{
    return $this->dropTables()
        && parent::uninstall();
}

private function dropTables(): bool
{
    $tables = [
        'mymodule_item_category',  // Primero las tablas con FK
        'mymodule_item_lang',
        'mymodule_item',
    ];

    foreach ($tables as $table) {
        if (!Db::getInstance()->execute(
            'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . $table . '`'
        )) {
            return false;
        }
    }

    return true;
}

// ── IMPORTANTE: El orden importa ──
// Si hay FOREIGN KEYS, eliminar primero las tablas hijas
// Si no hay FK, el orden no importa pero es buena practica
// empezar por las tablas de relacion y lang
```

## Tablas con ObjectModel

*ObjectModel para la tabla personalizada*

```php
<?php

// Archivo: classes/MyModuleItem.php
class MyModuleItem extends ObjectModel
{
    public $id_category;
    public $reference;
    public $price;
    public $active;
    public $sort_order;
    public $name;        // multilang
    public $description; // multilang
    public $date_add;
    public $date_upd;

    public static $definition = [
        'table'     => 'mymodule_item',
        'primary'   => 'id_mymodule_item',
        'multilang' => true,
        'fields'    => [
            'id_category' => ['type' => self::TYPE_INT,    'validate' => 'isUnsignedId'],
            'reference'   => ['type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 64],
            'price'       => ['type' => self::TYPE_FLOAT,  'validate' => 'isPrice'],
            'active'      => ['type' => self::TYPE_BOOL,   'validate' => 'isBool'],
            'sort_order'  => ['type' => self::TYPE_INT,    'validate' => 'isUnsignedInt'],
            'date_add'    => ['type' => self::TYPE_DATE,   'validate' => 'isDate'],
            'date_upd'    => ['type' => self::TYPE_DATE,   'validate' => 'isDate'],
            'name'        => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 255],
            'description' => ['type' => self::TYPE_HTML,   'lang' => true, 'validate' => 'isCleanHtml'],
        ],
    ];

    // Registrar la clase en el autoloader de PS:
    // En mymodule.php:
    // if (!defined('_PS_VERSION_')) { exit; }
    // require_once __DIR__ . '/classes/MyModuleItem.php';
}
```

## Modificar tablas en upgrades

*upgrade/upgrade_mymodule_1_1_0.php — ALTER TABLE*

```php
<?php

/**
 * Upgrade desde 1.0.x hasta 1.1.0
 * Agrega columna 'sku' y un indice a mymodule_item.
 */
if (!defined('_PS_VERSION_')) {
    exit;
}

function upgrade_module_1_1_0(MyModule $module): bool
{
    $db  = Db::getInstance();
    $sql = [];

    // ── Agregar columna nueva ──
    $sql[] = 'ALTER TABLE `' . _DB_PREFIX_ . 'mymodule_item`
        ADD COLUMN `sku` VARCHAR(64) NOT NULL DEFAULT \'\'  AFTER `reference`';

    // ── Agregar indice ──
    $sql[] = 'ALTER TABLE `' . _DB_PREFIX_ . 'mymodule_item`
        ADD INDEX `sku` (`sku`)';

    // ── Columna en tabla lang ──
    $sql[] = 'ALTER TABLE `' . _DB_PREFIX_ . 'mymodule_item_lang`
        ADD COLUMN `short_description` VARCHAR(512) NOT NULL DEFAULT \'\'  AFTER `name`';

    foreach ($sql as $query) {
        if (!$db->execute($query)) {
            return false;
        }
    }

    // ── Actualizar version en Configuration ──
    Configuration::updateValue('MYMODULE_VERSION', '1.1.0');
    return true;
}
```

## Buenas practicas

| Practica | Por que |
| --- | --- |
| Siempre usar CREATE TABLE IF NOT EXISTS | Evita errores si la tabla ya existe |
| Siempre usar DROP TABLE IF EXISTS | Evita errores si la tabla no existe |
| Prefijo _DB_PREFIX_ en todos los nombres | Permite instalaciones con prefijo personalizado |
| InnoDB como motor | Soporte de transacciones y FK |
| utf8mb4 como charset | Soporte completo de Unicode y emojis |
| Indices en columnas de busqueda/filtro | Mejora el rendimiento de queries |
| Columnas date_add y date_upd en toda tabla | Auditoria y depuracion |
| Verificar FK antes de DROP en uninstall | Evitar errores de integridad referencial |


---

*Fuente: [https://ayudaprestashop.es/database/custom-tables](https://ayudaprestashop.es/database/custom-tables). Version Markdown generada automaticamente para consumo por LLMs.*
