🔍 Queries y datos en multitienda

Actualizado: 2024-12-01

En multitienda, cada query debe considerar el contexto de tienda. Un modulo que funciona en tienda unica puede fallar en multitienda si no filtra correctamente por id_shop.

#Configuration en multitienda

Leer y escribir configuracion por tienda
php
<?php

// ── Configuration::get() respeta el contexto de tienda automaticamente ──
$valor = Configuration::get('MI_MODULO_OPCION');
// Busca en: tienda actual → grupo de tiendas → global

// ── Leer valor de una tienda especifica ──
$valorTienda2 = Configuration::get('MI_MODULO_OPCION', null, null, 2);
// Firma: get($key, $id_lang = null, $id_shop_group = null, $id_shop = null)

// ── Escribir valor para la tienda actual ──
Configuration::updateValue('MI_MODULO_OPCION', 'valor');
// Guarda en ps_configuration con id_shop = tienda actual

// ── Escribir valor global (todas las tiendas) ──
Configuration::updateGlobalValue('MI_MODULO_OPCION', 'valor_global');
// id_shop = 0, id_shop_group = 0

// ── Escribir valor para tienda especifica ──
$oldShop = Shop::getContext();
Shop::setContext(Shop::CONTEXT_SHOP, 2);
Configuration::updateValue('MI_MODULO_OPCION', 'valor_tienda_2');
Shop::setContext($oldShop);

// ── Eliminar configuracion por tienda ──
Configuration::deleteByName('MI_MODULO_OPCION');
// Elimina de TODAS las tiendas

#ObjectModel y id_shop

ObjectModel en contexto multitienda
php
<?php

// ── ObjectModel con multishop ──
// En la definicion de la clase:
class MyEntity extends ObjectModel
{
    public static $definition = [
        'table'     => 'mymodule_entity',
        'primary'   => 'id_mymodule_entity',
        'multilang' => true,
        'multishop' => true,  // ← Habilitar multitienda
        'fields'    => [
            'active' => ['type' => self::TYPE_BOOL, 'shop' => true],
            // 'shop' => true → este campo se guarda POR tienda
            // 'shop' => false o ausente → campo global
            'name'   => ['type' => self::TYPE_STRING, 'lang' => true, 'shop' => true],
            'config' => ['type' => self::TYPE_STRING, 'shop' => false], // Global
        ],
    ];
}

// Cuando multishop = true, PS crea automaticamente:
// ps_mymodule_entity          (datos globales)
// ps_mymodule_entity_shop     (datos por tienda)
// ps_mymodule_entity_lang     (si multilang)

// ── Guardar asociacion con tienda ──
$entity = new MyEntity();
$entity->active = true;
$entity->name   = 'Mi entidad';
$entity->id_shop_list = [1, 2]; // Asociar a tiendas 1 y 2
$entity->add();

// ── Leer filtrando por tienda ──
$entity = new MyEntity($id); // Usa la tienda actual del contexto

#Queries con filtro de tienda

Filtrar queries por id_shop
php
<?php

// ── Obtener id_shop actual ──
$idShop = (int) Context::getContext()->shop->id;

// ── Query filtrada por tienda ──
$results = Db::getInstance()->executeS(
    (new DbQuery())
        ->select('e.*, es.active')
        ->from('mymodule_entity', 'e')
        ->innerJoin('mymodule_entity_shop', 'es',
            'e.id_mymodule_entity = es.id_mymodule_entity AND es.id_shop = ' . $idShop)
        ->where('es.active = 1')
        ->orderBy('e.position ASC')
);

// ── Con Shop::addSqlRestriction (metodo recomendado) ──
$sql = 'SELECT p.*, ps.active, ps.price
        FROM `' . _DB_PREFIX_ . 'product` p
        ' . Shop::addSqlAssociation('product', 'p') . '
        WHERE ps.active = 1';
// addSqlAssociation genera:
// INNER JOIN ps_product_shop ps ON p.id_product = ps.id_product
//   AND ps.id_shop IN (tiendas del contexto actual)

// ── Para tablas custom, usar Shop::addSqlRestriction ──
$sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'mymodule_data` md
        WHERE 1 ' . Shop::addSqlRestriction(false, 'md');
// Añade: AND md.id_shop = X (o IN (X, Y) segun contexto)

#Datos compartidos vs por tienda

DatoCompartidoPor tiendaRecomendacion
Producto (base)SiPrecio, stock, activoCrear producto una vez, personalizar por tienda
CategoriaSi (estructura)Nombre, desc, activoMisma estructura, contenido diferente
ClienteConfigurableConfigurableCompartir clientes entre tiendas es lo habitual
PedidoNoSi (pertenece a una tienda)Cada pedido es de una tienda especifica
TransportistaNoSiDiferentes transportistas por tienda
CMS PageSi (ID)Contenido por tiendaMisma pagina, diferente contenido
Modulo configDependeDependeUsar Configuration con id_shop
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.