Esta vez toca una entrada algo más técnica en la que vamos a explicar cómo hacer uso de los servicios de Symfony que PrestaShop incorporó en sus versiones 1.7/8. Para ello vamos a crear un pequeño módulo que muestre en el listado de pedidos el peso total del pedido, pero vamos por partes.
¿Qué es Symfony?
Symfony es un framework de PHP, pero ¿Qué es un framework PHP? Un framework PHP son un conjunto de herramientas, que nos permiten crear aplicaciones web modulares basadas en el patrón vista, modelo, controlador. La utilización de un framework nos permite la creación de aplicaciones más seguras, robustas y escalables, pero además nos facilita componentes reutilizables que nos ahorrarán un montón de tiempo en nuestros desarrollos. Algunos de estos componentes son, enrutadores, eventos, logs, gestión de sesiones, contenedor de servicios, etc. Los componentes que nos interesan en este caso son los Contenedores de Servicios.
¿Qué es un servicio en Symfony?
La idea es pensar en un servicio con un objeto de una clase que realiza una tarea concreta y reutilizable, como por ejemplo, llamadas a una API, algún tipo de cálculo, consulta a base de datos, etc.
Estos servicios se registran en el contenedor de servicios de Symfony, el cual se encarga de crearlo y resolver sus dependencias cuando lo necesitamos, el uso de servicios nos permite tener un código más limpio y reutilizable.
PrestaShop y Symfony
A partir de la versión 1.7 PrestaShop incorporó Symfony para mejorar su Backend, a día de hoy siguen incorporando nuevos apartados en Symfony con el lanzamiento de cada versión, conviviendo con el sistema antiguo (legacy).
El uso de Symfony permite un backend más rápido y seguro, además de que permite extender su funcionalidad a través de los servicios propios del core y los que podamos inyectar.
¿Cómo crear e inyectar un servicio con un módulo de PrestaShop?
Lo mejor es verlo con un ejemplo real, así que vamos a presentaros un módulo que lo que haces es crear una nueva columna en el listado de pedidos del backend, que incluye el peso total del pedido. Vamos a ello:
Estructura del módulo
El módulo se va a llamar tprestorderweight y se creará en la carpeta modules siguiente la siguiente estructura:
- Carpeta modules
- Carpeta tprestorderweight
- tprestorderweight.php (archivo principal del módulo)
- Carpeta config
- services.yml (registro del servicio)
- Carpeta src
- Carpeta Service
- OrderWeightService.php (lógica del servicio)
- Carpeta Service
- composer.json
- Carpeta tprestorderweight
Registrar el servicio
services.yml
services:
tprestorderweight.order_weight:
class: Tprest\OrderWeight\Service\OrderWeightService
public: true
arguments:
- '@doctrine.dbal.default_connection'
En este archivo indicamos:
ID del servicio: será un nombre único, en este caso: tprestorderweight.order_weight
Para recuperarlo y poder utilizarlo en un hook se utilizará el siguiente código:
$container = \PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance();
$svc = $container->get('tprestorderweight.order_weight');
namespace: Tprest\OrderWeight\Service\OrderWeightService nombre de la clase con namespace que utilizará el servicio.
public: true: permite obtenerlo desde un módulo/hook, de lo contrario no puedes pedirlo utilizando el método get.
arguments: lista de dependencias que el contenedor pasa al constructor del servicio.
En nuestro caso tenemos
public function __construct(private Connection $conn)
así que recibimos una instancia de doctrine.dbal.default_connection que nos proporciona la conexión con la base de datos.
Cada parámetro se separa con un – y será un argumento en el constructor del servicio.
Implementar la lógica del servicio
OrderWeightService.php
// modules/tprestorderweight/src/Service/OrderWeightService.php
namespace Tprest\OrderWeight\Service;
use Doctrine\DBAL\Connection;
class OrderWeightService
{
public function __construct(private Connection $conn) {}
public function getOrderWeight(int $orderId): float
{
$sql = 'SELECT COALESCE(SUM(od.product_weight * od.product_quantity), 0)
FROM ' . _DB_PREFIX_ . 'order_detail od
WHERE od.id_order = :id';
$val = method_exists($this->conn, 'fetchOne')
? $this->conn->fetchOne($sql, ['id' => $orderId])
: $this->conn->executeQuery($sql, ['id' => $orderId])->fetchColumn(0);
return round((float) $val, 3);
}
}
Esta es la lógica del servicio, en el constructor recibimos una conexión a la base de datos y en el método getOrderWeight ejecutamos una consulta que obtiene el peso total de un pedido.
Autoload PSR-4
composer.json
// modules/tprestorderweight/composer.json
{
"name": "tprest/tprestorderweight",
"type": "prestashop-module",
"autoload": { "psr-4": { "Tprest\OrderWeight\": "src/" } }
}
En la raíz del módulo ejecutar: composer dump-autoload
Este composer registra el namespace (PSR-4) para que el core de PrestaShop encuentre la clase sin necesidad de utilizar «require» manuales.
Los campos:
- name: identificador del paquete.
- type: se trata de un metadato que indica que se trata de un módulo PrestaShop.
- autoload: psr-4 mapa de namespaces:
- Indica que cada clase que empiece por Tprest\OrderWeight\ se aloje en la carpeta src del proyecto
- Entonces Tprest\OrderWeight\Service\OrderWeightService se cargará desde: modules/tprestorderweight/src/Service/OrderWeightService.php
Archivo del módulo
tprestorderweight.php
<?php
if (!defined('_PS_VERSION_')) exit;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\DataColumn;
use PrestaShop\PrestaShop\Core\Grid\Definition\GridDefinitionInterface;
use PrestaShop\PrestaShop\Core\Grid\Data\GridData;
use PrestaShop\PrestaShop\Core\Grid\Record\RecordCollection;
class Tprestorderweight extends Module
{
public function __construct()
{
$this->name = 'tprestorderweight';
$this->version = '1.0.1';
$this->author = 'Labox';
$this->tab = 'administration';
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Peso en listado de pedidos (tprest)');
$this->description = $this->l('Añade una columna con el peso total del pedido al grid de pedidos usando un servicio Symfony.');
}
public function install()
{
return parent::install()
&& $this->registerHook('actionOrderGridDefinitionModifier')
&& $this->registerHook('actionOrderGridDataModifier'); // ← usa este
}
/**
* Añade la columna "Peso (kg)" al grid de pedidos.
*/
public function hookActionOrderGridDefinitionModifier(array $params)
{
/** @var GridDefinitionInterface $definition */
$definition = $params['definition'];
// la clave 'total_weight' debe existir en los records después del presenter modifier
$definition->getColumns()->addAfter(
'total_paid_tax_incl',
(new DataColumn('total_weight'))
->setName($this->l('Peso (kg)'))
->setOptions(['field' => 'total_weight'])
);
}
public function hookActionOrderGridDataModifier(array $params)
{
if (empty($params['data']) || !($params['data'] instanceof GridData)) {
\PrestaShopLogger::addLog('[tprestorderweight] GridData no presente o tipo no válido en actionOrderGridDataModifier');
return;
}
/** @var GridData $data */
$data = $params['data'];
// Servicio desde el contenedor de Symfony (más fiable que $this->get())
$container = \PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance();
/** @var \Tprest\OrderWeight\Service\OrderWeightService $weightService */
$weightService = $container->get('tprestorderweight.order_weight');
// Construimos un nuevo array de records con la clave total_weight añadida
$modified = [];
foreach ($data->getRecords() as $record) { // $record es array
// Ajusta si tu versión/override usa otra clave para el ID
$idOrder = (int)($record['id_order'] ?? $record['id_order_id'] ?? $record['id'] ?? 0);
$record['total_weight'] = $idOrder ? $weightService->getOrderWeight($idOrder) : 0.0;
// Para depurar claves:
// \PrestaShopLogger::addLog('[tprestorderweight] keys: '.implode(',', array_keys($record)));
$modified[] = $record;
}
// Reemplazamos el GridData completo
$params['data'] = new GridData(
new RecordCollection($modified),
$data->getRecordsTotal(),
$data->getQuery() // mantiene paginación/filtros actuales
);
}
}
Utilizando el hook actionOrderGridDefinitionModifier añadimos una columna al grid del listado de pedidos, en este caso Peso (Kg).
Para inyectar los datos de la nueva columna utilizamos el hook actionOrderGridDataModifier, este hook recibe un GridData, el cual es modificado añadiendo una nueva columna a cada registro con la clave total_weight por pedido que nos devuelve nuestro servicio.
Conclusión
Symfony brinda a PrestaShop una base moderna y flexible. Un servicio no es más que una pieza de lógica reutilizable registrada en el contenedor, la cual puedes declarar en servicies.yml e inyectar en hooks o controladores para cubrir necesidades concretas del Back Office y Front Office con un código más limpio y fácil de mantener.
Este desarrollo ha sido validado en la versión 8.1.6 de PrestaShop. En otras versiones podrían presentarse ligeras variaciones. Si necesitas asistencia con la adaptación, recuerda que cuentas con nuestro servicio de soporte para ayudarte.
Con esto y más ¡Tu tienda online siempre preparada!


