Desarrollo de módulos personalizados en PrestaShop 8
Introducción al Desarrollo de Módulos en PrestaShop 8
El ecosistema de comercio electrónico ha evolucionado drásticamente, y con él, las herramientas que permiten personalizar y escalar las tiendas online. PrestaShop 8 representa un salto cualitativo, no solo por su rendimiento y seguridad, sino por su arquitectura moderna basada en Symfony PrestaShop. Atrás quedaron los días de depender exclusivamente de controladores legacy y clases helper. Hoy, el desarrollo de módulos PrestaShop personalizados requiere entender los principios de un framework robusto, inyección de dependencias y el patrón MVC.
Este artículo no es un simple tutorial; es una guía exhaustiva para SysAdmins y desarrolladores que desean dominar la creación de módulos desde cero en la versión 8. Abordaremos desde la estructura básica hasta técnicas avanzadas como hooks, servicios y la integración con Doctrine. Prepárate para ensuciarte las manos con código real.
Estructura Fundamental de un Módulo en PrestaShop 8
Antes de escribir una sola línea de lógica de negocio, debemos comprender la anatomía de un módulo. La estructura de directorios y archivos sigue convenciones estrictas, aunque ahora con un toque de Symfony.
Archivos Esenciales y el Manifiesto
Todo módulo comienza con un archivo principal PHP y un manifiesto XML (o YAML en algunos casos modernos). El nombre del directorio y la clase principal deben coincidir exactamente.
mimodulo/
├── mimodulo.php # Clase principal del módulo
├── config.xml # Manifiesto (generado automáticamente)
├── logo.png # Logo de 32x32px
├── controllers/ # Controladores (front y admin)
│ ├── front/
│ └── admin/
├── src/ # Código Symfony puro (Servicios, Formularios, Entidades)
│ ├── Entity/
│ ├── Repository/
│ ├── Form/
│ └── Controller/
├── views/ # Plantillas Twig
│ ├── templates/
│ └── layouts/
├── translations/ # Traducciones
├── upgrade/ # Scripts de actualización
└── vendor/ # Dependencias (si aplica)
El archivo mimodulo.php debe contener una clase que extienda Module e implemente métodos como install(), uninstall(), y getContent().
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
class MiModulo extends Module
{
public function __construct()
{
$this->name = 'mimodulo';
$this->tab = 'front_office_features';
$this->version = '1.0.0';
$this->author = 'Tu Nombre';
$this->need_instance = 0;
$this->ps_versions_compliancy = ['min' => '8.0', 'max' => _PS_VERSION_];
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Mi Módulo Personalizado');
$this->description = $this->l('Descripción del módulo.');
$this->confirmUninstall = $this->l('¿Estás seguro de desinstalar?');
}
public function install()
{
return parent::install()
&& $this->registerHook('displayHeader')
&& $this->installDb();
}
public function uninstall()
{
return $this->uninstallDb() && parent::uninstall();
}
}
[TIP] Siempre verifica la compatibilidad con la versión de PrestaShop usando
ps_versions_compliancy. En PS8, el mínimo es 8.0.
Migración a Symfony: El Corazón de PrestaShop 8
Una de las mayores novedades en desarrollo PrestaShop 8 es la integración profunda con Symfony. Aunque el núcleo sigue teniendo capas legacy, ahora podemos crear controladores Symfony nativos, servicios y formularios.
Creando un Controlador Symfony para el Front Office
PrestaShop 8 permite definir rutas Symfony directamente en el módulo. Para ello, creamos un controlador dentro de src/Controller/FrontController.php.
<?php
namespace MiModulo\Controller\Front;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use PrestaShopBundle\Controller\FrontController;
class MiFrontController extends FrontController
{
public function indexAction(Request $request): Response
{
// Lógica personalizada
$datos = ['mensaje' => 'Hola desde Symfony en PS8'];
return $this->render('@Modules/mimodulo/views/templates/front/mi_vista.html.twig', $datos);
}
}
Para que esta ruta funcione, debemos registrarla en un archivo de configuración de rutas. PrestaShop 8 lee automáticamente config/routes.yml dentro del módulo.
# config/routes.yml
mimodulo_front:
path: /mi-ruta-personalizada
methods: [GET]
defaults:
_controller: 'MiModulo\Controller\Front\MiFrontController::indexAction'
[WARNING] Asegúrate de que el namespace del controlador coincida con la estructura de directorios. El autoloading de Composer debe estar configurado en
composer.jsonsi usas dependencias externas.
Uso de Servicios y el Contenedor de Dependencias
La inyección de dependencias es clave en Symfony PrestaShop. Podemos definir servicios personalizados en config/services.yml.
# config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: true
MiModulo\Service\ProcesadorDatos:
class: MiModulo\Service\ProcesadorDatos
arguments:
- '@doctrine.orm.default_entity_manager'
Luego, en cualquier controlador o hook, podemos acceder al servicio mediante el contenedor:
$procesador = $this->get('MiModulo\Service\ProcesadorDatos');
Hooks: La Columna Vertebral de la Personalización
Los hooks son el mecanismo para enganchar nuestro código en puntos estratégicos de la tienda. En PrestaShop 8, los hooks se manejan de manera similar, pero ahora podemos aprovechar los servicios de Symfony.
Registro y Ejecución de un Hook
Para registrar un hook, lo hacemos en el método install():
$this->registerHook('displayProductAdditionalInfo');
Luego, implementamos el método hook:
public function hookDisplayProductAdditionalInfo($params)
{
// $params contiene información del producto, contexto, etc.
$productoId = $params['id_product'];
// Podemos usar un servicio
$servicio = $this->get('MiModulo\Service\ProcesadorDatos');
$infoExtra = $servicio->obtenerInfo($productoId);
$this->context->smarty->assign('info_extra', $infoExtra);
return $this->display(__FILE__, 'views/templates/hook/product_extra.tpl');
}
[INFO] Aunque PS8 soporta Twig, los hooks legacy siguen usando Smarty. Si tu hook se ejecuta en una zona que usa Twig (como el back office), puedes devolver directamente HTML o usar Twig.
Hooks Modernos con Symfony
Para hooks dentro del back office Symfony, podemos devolver directamente un Response:
public function hookDisplayAdminProductsExtra($params)
{
$form = $this->createForm(MiFormType::class);
return $this->render('@Modules/mimodulo/views/templates/admin/product_extra.html.twig', [
'form' => $form->createView(),
]);
}
Base de Datos y Doctrine ORM
Una de las tareas más comunes en el desarrollo de módulos PrestaShop personalizados es la gestión de datos. En PS8, podemos usar Doctrine directamente, aunque también sigue siendo válido el uso de Db::getInstance() para operaciones simples.
Creación de Entidades y Migraciones
Para usar Doctrine, debemos definir una entidad en src/Entity/.
<?php
namespace MiModulo\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="ps_mimodulo_datos")
* @ORM\Entity(repositoryClass="MiModulo\Repository\DatosRepository")
*/
class Datos
{
/**
* @ORM\Id
* @ORM\Column(name="id_dato", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="nombre", type="string", length=255)
*/
private $nombre;
// getters y setters...
}
Luego, en el método install() del módulo, podemos crear las tablas usando el esquema de Doctrine:
public function installDb()
{
$schema = $this->get('doctrine.orm.default_entity_manager')->getConnection()->getSchemaManager();
// Crear tabla manualmente o usar migraciones
$sql = "CREATE TABLE IF NOT EXISTS `ps_mimodulo_datos` (
`id_dato` INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`nombre` VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
return Db::getInstance()->execute($sql);
}
[WARNING] Las migraciones automáticas de Doctrine no están integradas por defecto en el ciclo de instalación de módulos. Es recomendable manejar la creación de tablas manualmente o mediante scripts SQL en
install/.
Configuración y Formularios en el Back Office
Todo módulo que se precie necesita una página de configuración. En PrestaShop 8, podemos crear formularios Symfony modernos.
Creando un Formulario con Symfony Forms
Definimos un FormType en src/Form/.
<?php
namespace MiModulo\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
class ConfigType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('MI_MODULO_TITULO', TextType::class, [
'label' => 'Título personalizado',
'constraints' => [new NotBlank()],
'data' => \Configuration::get('MI_MODULO_TITULO'),
]);
}
}
Luego, en el método getContent() de la clase principal, procesamos el formulario:
public function getContent()
{
$form = $this->createForm(ConfigType::class);
$form->handleRequest($this->get('request_stack')->getCurrentRequest());
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
\Configuration::updateValue('MI_MODULO_TITULO', $data['MI_MODULO_TITULO']);
$this->confirmations = $this->l('Configuración guardada');
}
return $this->render('@Modules/mimodulo/views/templates/admin/config.html.twig', [
'form' => $form->createView(),
]);
}
Buenas Prácticas y Optimización para SysAdmins
Como SysAdmin, tu prioridad es la estabilidad y el rendimiento. Aquí tienes algunas reglas de oro.
Gestión de Caché y Compilación
PrestaShop 8 utiliza un caché de Symfony muy agresivo. Cuando desarrolles, desactívalo temporalmente:
# En el archivo .env de la tienda (no del módulo)
PS_DEV_MODE=1
PS_CACHE_ENABLED=0
Para limpiar cachés específicos del módulo:
php bin/console cache:clear --env=prod
php bin/console prestashop:module reset mimodulo
Seguridad y Permisos
Nunca confíes en los datos del usuario. Usa los validadores de Symfony y escapa la salida en Twig.
{{ mi_variable|escape('html') }}
Además, asegúrate de que los archivos del módulo tengan los permisos correctos:
chmod -R 755 /ruta/a/modulos/mimodulo/
chmod 644 /ruta/a/modulos/mimodulo/mimodulo.php
Pruebas con PHPUnit y Behat
Para módulos complejos, integra pruebas unitarias. PrestaShop 8 soporta PHPUnit 9.x. Crea un phpunit.xml en la raíz del módulo:
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="MiModulo">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
Conclusión y Próximos Pasos
El desarrollo de módulos PrestaShop personalizados en la versión 8 ya no es un arte oscuro. Con la integración de Symfony PrestaShop, tienes a tu disposición un framework moderno, inyección de dependencias, formularios robustos y un sistema de plantillas Twig. Sin embargo, el legacy sigue presente; saber navegar entre ambos mundos es lo que diferencia a un desarrollador experto.
Para profundizar, te recomiendo explorar la documentación oficial de PrestaShop 8 sobre hooks y servicios, y estudiar módulos core como ps_themecustomizer para ver ejemplos reales de integración Symfony.
[TIP] Únete a la comunidad de PrestaShop en Slack y al foro de desarrolladores. Las mejores soluciones suelen nacer de la colaboración.
Ahora, ve y construye ese módulo que tu tienda necesita. ¡El código te espera!
