🏗️ Arquitectura PS 9 vs PS 8 — Guia de diferencias
#Por que PS 9 es diferente
PrestaShop 9 marca la transicion definitiva de legacy a Symfony. Mientras PS 8 era un hibrido (legacy + Symfony coexistiendo), PS 9 establece que toda nueva funcionalidad debe ser Symfony. El legacy se mantiene por retrocompatibilidad pero no recibe mejoras.
| Aspecto | PS 8.x | PS 9.x |
|---|---|---|
| Symfony version | 4.4 LTS / 5.4 LTS | 6.4 LTS |
| PHP minimo | 7.2 (8.0 recomendado) | 8.1 (8.5 recomendado) |
| Admin controllers | Legacy + Symfony ambos validos | Symfony preferido, legacy solo retrocompat |
| Service Locator | $this->get() disponible | $this->get() deprecated → Constructor Injection |
| Context statico | Permitido | Evitar en servicios, inyectar datos necesarios |
| CQRS | Opcional | Obligatorio para nueva arquitectura admin |
| Tema por defecto | Classic (BS4 alpha) | Hummingbird (BS5.3.3) |
| jQuery | Obligatorio | Deprecated (sera eliminado en PS 10) |
| Domain separation | Recomendada | Obligatoria en nuevo codigo |
| Product page | V1 (legacy) + V2 (8.1+) | Solo V2 |
| Feature flags | Basico | Sistema completo con estados (beta/stable) |
#Symfony 6.4 LTS
El salto de Symfony 4.4/5.4 a 6.4 trae cambios importantes:
#CQRS obligatorio en admin
PrestaShop 9 usa el patron CQRS (Command Query Responsibility Segregation) para todas las operaciones admin. Esto separa las lecturas (Queries) de las escrituras (Commands):
use PrestaShop\PrestaShop\Core\Domain\Product\Query\GetProductForEditing;
use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductForEditing;
// Query — lectura
$query = new GetProductForEditing($productId);
/** @var ProductForEditing $result */
$result = $this->commandBus->handle($query);
$name = $result->getBasicInformation()->getLocalizedNames();
use PrestaShop\PrestaShop\Core\Domain\Product\Command\UpdateProductCommand;
// Command — escritura
$command = new UpdateProductCommand($productId);
$command->setLocalizedNames([
1 => 'Mi Producto Actualizado',
2 => 'My Updated Product',
]);
$command->setPrice('29.99');
$this->commandBus->handle($command);
#Dependency Injection — el cambio central
El cambio mas importante para modulos: inyectar servicios via constructor, no via $this->get():
services:
_defaults:
autowire: true
autoconfigure: true
MyModule\Controller\Admin\MyController:
arguments:
$commandBus: '@prestashop.core.command_bus'
$queryBus: '@prestashop.core.query_bus'
$translator: '@translator'
tags:
- { name: 'controller.service_arguments' }
MyModule\Service\MyService:
arguments:
$configuration: '@prestashop.adapter.legacy.configuration'
$entityManager: '@doctrine.orm.entity_manager'
#Context::getContext() — como evitarlo
En PS 9, Context::getContext() sigue funcionando en el module main class, pero no debe usarse dentro de servicios:
class MyService
{
public function getShopId(): int
{
return (int) Context::getContext()->shop->id; // ❌ Anti-pattern en PS 9
}
}
class MyService
{
public function __construct(
private readonly ShopContext $shopContext,
) {}
public function getShopId(): int
{
return $this->shopContext->getContextShopID(); // ✅
}
}
// services.yml:
// MyModule\Service\MyService:
// arguments:
// $shopContext: '@prestashop.adapter.shop.context'
#Legacy vs Modern: que queda
| Componente | Legacy (aun funciona) | Modern (preferido en PS 9) |
|---|---|---|
| Admin controllers | AdminController extends ModuleAdminController | Symfony Controller + routing YAML |
| Formularios | HelperForm / HelperList | Symfony FormType + Grid System |
| ORM | ObjectModel | Doctrine ORM entities |
| Queries | Db::getInstance()->executeS() | Doctrine DBAL / Repository |
| Templates BO | Smarty .tpl | Twig .html.twig |
| Templates FO | Smarty .tpl | Smarty .tpl (Twig previsto para PS 10) |
| Traducciones | $this->l('...') | $this->trans('...', [], 'Modules.X.Admin') |
| Eventos | Hook dispatcher | Hook dispatcher + Symfony EventDispatcher |
| Config | Configuration::get/updateValue() | Igual + DataConfiguration pattern |
| CLI | — | bin/console con comandos custom |
#Modulo PS 9: estructura recomendada
mi_modulo/
├── mi_modulo.php # Clase principal (install, hooks)
├── composer.json # Autoloading PSR-4
├── config/
│ ├── routes.yml # Rutas Symfony para controllers admin
│ └── services.yml # Servicios, DI, autowiring
├── src/
│ ├── Controller/
│ │ └── Admin/ # Controladores Symfony admin
│ ├── Entity/ # Doctrine ORM entities
│ ├── Repository/ # Doctrine repositories
│ ├── Form/
│ │ ├── Type/ # FormType classes
│ │ └── DataConfiguration/ # Data providers
│ ├── Grid/
│ │ ├── Definition/ # Grid definitions
│ │ └── Query/ # Grid query builders
│ ├── Command/ # Console commands
│ └── Service/ # Business logic
├── views/
│ ├── templates/
│ │ ├── admin/ # Twig templates (BO)
│ │ └── front/ # Smarty templates (FO)
│ ├── css/
│ └── js/
├── upgrade/ # Upgrade scripts
└── tests/ # PHPUnit tests
#Nuevos comandos CLI en PS 9.1
| Comando | Descripcion |
|---|---|
| prestashop:thumbnails:regenerate | Regenerar thumbnails de imagenes de producto |
| prestashop:search:index | Reconstruir el indice de busqueda desde CLI |
| prestashop:module:export-translations | Exportar traducciones de un modulo a ficheros |
# Regenerar thumbnails
php bin/console prestashop:thumbnails:regenerate --all
# Reindexar busqueda
php bin/console prestashop:search:index
# Exportar traducciones de un modulo
php bin/console prestashop:module:export-translations mi_modulo
#Feature Flags system
PS 9.1 utiliza un sistema de feature flags para activar/desactivar funcionalidades experimentales. Los flags se almacenan en ps_feature_flag con estados: beta, stable.
// Opcion 1: Via servicio (PS 9)
$featureFlagManager = $this->get('prestashop.core.feature_flag.manager');
if ($featureFlagManager->isEnabled('improved_shipment')) {
// Funcionalidad de multi-shipment activa
}
// Opcion 2: Via SQL directo (compatible PS 8+9)
$isEnabled = (bool) Db::getInstance()->getValue(
'SELECT state FROM ' . _DB_PREFIX_ . "feature_flag WHERE name = 'improved_shipment'"
);
Feature flags activos en PS 9.1:
| Flag | Estado | Descripcion |
|---|---|---|
| improved_shipment | Beta | Sistema Multi-Shipment (multiples envios por pedido) |
| discount | Beta | Nuevo sistema de descuentos con 4 tipos |
| tag | Beta | Sistema de tags modernizado con formularios Symfony |
#Detectar version de PS en tu modulo
// Detectar PS 9+
if (version_compare(_PS_VERSION_, '9.0.0', '>=')) {
// PS 9 — usar arquitectura moderna
// Constructor injection, CQRS, Twig admin
}
// Detectar PS 9.1+ (multi-shipment, discounts)
if (version_compare(_PS_VERSION_, '9.1.0', '>=')) {
// PS 9.1 — nuevos hooks disponibles
// Feature flags para shipment y discounts
}
// Detectar Hummingbird
$isHummingbird = (Context::getContext()->shop->theme_name === 'hummingbird');
// Patron hibrido PS 8 + PS 9
public function install()
{
$hooks = [
'displayHeader',
'displayFooter',
'actionValidateOrder',
];
// Hooks solo PS 9.1+
if (version_compare(_PS_VERSION_, '9.1.0', '>=')) {
$hooks[] = 'actionModuleEnable';
$hooks[] = 'actionModuleDisable';
$hooks[] = 'actionConfigurationUpdateValueBefore';
}
return parent::install() && $this->registerHook($hooks);
}