Módulos multitienda PrestaShop 9: evitar fugas del ámbito de datos
El error multitienda más peligroso suele ser una consulta válida a la que le falta una condición. Devuelve un registro real, supera las comprobaciones habituales de tipos y guarda correctamente, pero en el estado de la tienda equivocada. Una instalación de desarrollo con una sola tienda rara vez revela el error.
Los módulos multitienda fiables empiezan por la propiedad de los datos. Decida qué valores son globales, cuáles pertenecen a un grupo de tiendas y cuáles a una sola tienda. Después traslade esa decisión a claves, lecturas, escrituras, tareas en segundo plano y cachés.
Este es un artículo general de diseño de módulos PrestaShop 9. No describe nuevas funciones administrativas de 9.2, y los ejemplos SQL usan una tabla de módulo deliberadamente ilustrativa, no un prefijo de producción o esquema del núcleo supuestos.
Cuando el trabajo delimitado se ejecuta en segundo plano, aplique un diseño fiable de tareas cron transmitiendo la identidad explícita de tienda a cada ejecución.
Modele la propiedad antes de añadir una columna id_shop
Añadir id_shop a una tabla no hace que el módulo sea seguro en multitienda. Todas las operaciones deben usarlo de forma coherente, y algunos datos no deben duplicarse por tienda en absoluto.
Considere una correspondencia de referencias externas. Si el sistema externo asigna una referencia distinta al mismo producto en cada tienda, el ámbito de tienda pertenece a la identidad de la correspondencia. Si la referencia es realmente global, duplicarla puede crear copias contradictorias. Determine primero la semántica del sistema externo.
Separe la asociación de entidades de la propiedad de valores. Un producto puede estar asociado a varias tiendas y un valor concreto del módulo diferir entre ellas. A la inversa, una entidad asociada a una tienda puede referirse a un registro de propiedad global.
Elabore una tabla de propiedad para cada conjunto de datos del módulo:
| Conjunto de datos | Propietario | Ámbito de lectura | Ámbito de escritura |
|---|---|---|---|
| Credenciales del destino externo | Ámbito de configuración elegido | Configuración efectiva explícita | Contexto autorizado |
| Correspondencia de productos | Definida por las reglas de identidad externa | Producto más ámbito requerido | Mismo límite de identidad |
| Progreso de tareas | Una partición lógica de tarea | Clave explícita de tienda/tarea | Proceso propietario |
| Vocabulario compartido de consulta | Global si realmente se comparte | Global | Acción controlada del administrador |
La tabla es un artefacto de diseño. Debe explicar por qué existe un ámbito, no limitarse a reflejar las columnas que resultaba más fácil añadir.
Delimite lecturas y escrituras con la misma identidad
Un defecto frecuente es una lectura correctamente delimitada seguida de una actualización de ámbito demasiado amplio. Otro es buscar por producto y tienda pero imponer unicidad solo por producto.
La siguiente consulta ilustra una lectura de correspondencias propias del módulo usando vinculación de parámetros. El nombre literal de la tabla es un ejemplo; un instalador real debe derivar su prefijo fiable de la configuración de la aplicación y su propio esquema declarado.
SELECT external_reference
FROM demo_product_mapping
WHERE id_shop = :shop_id
AND id_product = :product_id;
La escritura correspondiente debe usar el mismo límite de propiedad. No actualice únicamente por id_product después de leer con ambas condiciones. El llamante debe pasar un objeto de ámbito validado o un identificador explícito de tienda en vez de depender de un contexto global mutable dentro del repositorio.
Los identificadores dinámicos necesitan un tratamiento diferente de los valores de parámetros. Los parámetros vinculados protegen valores, no nombres arbitrarios de tablas. Nunca acepte un nombre de tabla o prefijo de un parámetro HTTP para hacer funcionar una consulta multitienda genérica.
Para una lectura por lotes, incluya el ámbito tanto en las uniones como en el predicado exterior. Una unión solo por producto puede añadir una fila del módulo de otra tienda aunque la consulta principal de la entidad esté bien delimitada.
Pruebe explícitamente el caso negativo: solicite el valor de la tienda A cuando solo B tenga uno. No devolver una correspondencia local puede ser correcto; tomar silenciosamente la correspondencia de B no es una opción predeterminada segura.
Haga que la unicidad exprese la regla de negocio
Si el modelo es una correspondencia por producto y tienda, su clave debe expresar ese par. Una clave única solo por producto rechaza registros válidos de otra tienda. No tener clave única permite duplicados en inserciones concurrentes.
Una definición ilustrativa de tabla del módulo es:
CREATE TABLE demo_product_mapping (
id_shop INT UNSIGNED NOT NULL,
id_product INT UNSIGNED NOT NULL,
external_reference VARCHAR(191) NOT NULL,
PRIMARY KEY (id_shop, id_product)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Esto no es una importación para una base de datos PrestaShop en producción. Solo demuestra la clave de propiedad. Un esquema real necesita las convenciones de instalación del módulo, tipos de identificadores verificados, estrategia de migración y cualquier restricción adicional de relaciones.
No añada unicidad sobre external_reference sin decidir si es única globalmente, por tienda o por destino. Si varios sistemas externos pueden asignar la misma cadena, la identidad del destino también puede pertenecer a la clave.
Antes de añadir una restricción a datos existentes, inspeccione y resuelva duplicados según una política definida. Eliminar la fila que resulte ser la segunda no es una estrategia de migración. Conserve pruebas del registro que se mantuvo y de por qué.
Una restricción de base de datos complementa la validación de la aplicación. Protege el invariante cuando dos solicitudes aparentemente válidas compiten.
Trate la configuración heredada como estado, no solo como valor
Una tienda puede heredar un valor o almacenar explícitamente uno igual. Se comportan de forma distinta cuando cambia el padre. Un módulo que lee el valor efectivo y lo vuelve a escribir incondicionalmente puede convertir accidentalmente la herencia en una modificación local.
La documentación de configuración de PrestaShop describe el acceso consciente del ámbito mediante ShopConstraint, y sus orientaciones sobre formularios distinguen la configuración local de los valores heredados. Siga el contrato de servicio pertinente para la versión de plataforma que admita. Configuración de tiendas, formularios de configuración.
Al implementar un guardado, distinga «establecer un valor local», «eliminar la modificación local» y «dejar este campo sin cambios». Esas operaciones no deben reducirse a una única asignación de valor.
El mismo cuidado se aplica a credenciales y destinos externos. Un proceso debe saber qué configuración efectiva utilizó y qué tienda estaba autorizado a procesar. Registrar el identificador del destino puede ayudar a diagnosticar problemas de ámbito sin exponer la credencial.
Evite inventar id_shop = 0 como convención universal de fila global para todos los conjuntos de datos. Los modelos de almacenamiento del núcleo y los módulos difieren. Siga la semántica específica del esquema y los servicios en vez de extender un valor centinela no documentado por el código.
Transmita el ámbito al trabajo en cola y programado
Una tarea en segundo plano debe contener suficiente identidad para ejecutarse sin una sesión de empleado: tienda, operación, identificadores de entidades pertinentes y una clave de negocio estable. Valide la tienda antes de cargar datos o configuración.
No ponga en cola solo un ID de producto y deje que un proceso posterior deduzca la tienda a partir del contexto global que tenga por casualidad. Los procesos persistentes pueden procesar varios mensajes, lo que hace especialmente difícil reproducir las fugas de contexto.
Cuando el código heredado requiera contexto implícito, aísle cuidadosamente la adaptación y restaure el estado anterior incluso si ocurre una excepción. Prefiera ámbitos explícitos en los límites de sus propios servicios para que la dependencia heredada no se extienda por todo el módulo.
Una tarea para todas las tiendas debe recorrer un conjunto autorizado deliberado, estableciendo y limpiando el ámbito adecuado para cada partición. «Todas las tiendas» debe ser una operación explícita con un resultado definido, no la consecuencia accidental de un argumento ausente.
Incluya el ámbito en las claves de bloqueo e idempotencia cuando el trabajo sea independiente por tienda. Un bloqueo global puede serializar innecesariamente trabajo no relacionado, mientras que una clave sin identidad de destino puede tratar dos exportaciones diferentes como duplicadas.
Dé a los datos en caché las mismas dimensiones que a la consulta
Una clave de caché forma parte del modelo de datos. Si un resultado depende de tienda, idioma, moneda o grupo de clientes, la clave y la estrategia de invalidación deben reflejar las dimensiones pertinentes.
Para una etiqueta de producto propia del módulo, una clave conceptual podría incluir el espacio de nombres del módulo, el ID de producto, de tienda, de idioma y una versión de representación. Es un patrón de diseño, no un formato prescrito de clave de caché PrestaShop.
Incluya solo dimensiones que realmente afecten al valor, pero no omita una por comodidad. Una consulta correcta a la base de datos seguida de una entrada compartida de caché puede seguir filtrando el resultado de la primera tienda a la segunda.
Pruebe solicitando la misma entidad en contextos distintos en ambos órdenes: A y después B, y B y después A. Si el resultado cambia con el orden de solicitudes, sospeche del contexto almacenado en caché o retenido. Las pruebas realizadas solo con caché vacía pueden ocultar esta clase de defecto.
La invalidación debe alcanzar todas las representaciones afectadas. Un cambio de valor global puede exigir invalidar varias salidas específicas de tienda; un cambio local no debe sobrescribir datos de otras tiendas durante la reconstrucción de la caché.
Use datos de prueba adversos para probar el aislamiento
Cree dos tiendas con valores deliberadamente contradictorios para una entidad compartida. Asígneles identificadores de destino externo diferentes y estados distintos de configuración heredada/local. Después ejercite cada lectura y escritura mediante las interfaces admitidas del módulo.
Las aserciones útiles incluyen:
- Una escritura en la tienda A deja intactas la fila y el valor efectivo de B.
- Una lectura en A nunca recurre a B sin una regla de negocio explícita.
- Una acción sobre todas las tiendas afecta exactamente al conjunto autorizado seleccionado.
- Una tarea en segundo plano procesa la tienda nombrada en su carga útil.
- Los resultados de caché siguen siendo correctos con cualquier orden de solicitudes.
- Las escrituras concurrentes duplicadas mantienen la identidad única declarada.
- Eliminar una modificación local restaura la herencia en vez de copiar un valor del padre.
Pruebe también asociaciones ausentes y entidades eliminadas. El módulo debe devolver un error o ausencia definidos, no elegir arbitrariamente una fila restante para que el flujo continúe.
Para un informe de regresión, conserve los dos ID de tienda, los valores distintos de prueba y la operación que cruzó el límite. Un caso mínimo de dos tiendas suele explicar el defecto con más claridad que una gran exportación de producción.
Convierta el aislamiento en un hábito de revisión
Durante la revisión de código, pregunte dónde entra el ámbito en el servicio, dónde se valida y si cada operación de persistencia y caché lo mantiene. Revise juntos los predicados de consulta y las restricciones únicas. Revise las cargas de los mensajes de segundo plano junto con los gestores que las consumen.
En módulos existentes, empiece por las escrituras de mayor impacto y las integraciones externas. Corregir una lectura delimitada no basta si la actualización o exportación correspondiente sigue usando valores globales predeterminados.
La corrección multitienda no es un modo especial que se añade al final del desarrollo. Es la concordancia entre el significado de los datos y cada límite que los maneja. Cuando esa concordancia es explícita, las fugas entre tiendas se convierten en fallos comprobables, no en informes misteriosos de clientes.
Para conocer el contexto de la plataforma subyacente, consulte nuestra guía de desarrollo de módulos PrestaShop 9 y aplique las comprobaciones específicas de versión anteriores a la instalación que esté evaluando.
Al elegir una extensión, explore los módulos PrestaShop 9 de PrestaSuite y contraste el soporte de versiones y los requisitos de integración documentados con sus propios resultados de pruebas.