¿Cómo establecer una ID de tienda en Mage_Catalog_Model_Resource_Product_Collection?

34

La tarea es trivial. Necesito obtener una lista de productos para ver una tienda en particular con un catálogo plano habilitado. La solución más obvia es la siguiente:

$collection = Mage::getResourceModel('catalog/product_collection')
    ->setStore($storeId);

De hecho, el setStore()método no hace ninguna diferencia aquí porque se llama después de que el _initSelect()método Mage_Catalog_Model_Resource_Product_Collectionobtiene el nombre de la tabla plana según la ID de la tienda. Como la ID de la tienda aún no está configurada, toma la ID de la tienda actual.

Por lo tanto, la solución obvia sería establecer una ID de tienda actual antes de obtener un modelo.

Mage::app()->setCurrentStore($storeId);

$collection = Mage::getResourceModel('catalog/product_collection');

Funcionará. Pero solo si necesita obtener una colección una vez. Si necesita obtener una colección en el ciclo o simplemente necesita dos colecciones consecutivas, no podrá establecer una tienda específica para ellas.

La razón es que la Mage_Catalog_Model_Resource_Product_Flatclase tiene su propia _storeIdpropiedad y en el constructor se establece en el ID de la tienda actual. Es por eso que se establecerá la primera vez. Entonces, por alguna razón (Dios sabe que espero que haya uno) en Mage_Eav_Model_Entity_Collection_Abstract::_initcada módulo de recursos se obtiene como un singleton. Entonces no hay constructor para la 2da llamada.

Todo esto se ve tan mal que estoy bastante seguro de que estoy equivocado y no es otro error de Magento (o dos). Espero que alguien pueda arrojar una luz sobre ello.

usuario487772
fuente
¿Tiene que usar getResourceModel () ya que esto le da la instancia? getModel ('catalog / resource_product_collection') podría funcionar.
Kristof en Fooman
No, es absolutamente lo mismo. Está instanciando el modelo de recursos singleton de cualquier manera.
user487772
Tim, agrégalo como respuesta, por favor.
Fabian Blechschmidt
@FabianBlechschmidt hecho.
user487772

Respuestas:

13

¿Qué versión de Magento es esta? Estos son mis resultados para Magento 1.9:

Catálogo plano habilitado:

El catálogo plano está indexado:

Algunos conjuntos de datos en una vista de tienda específica:

Código usado:

<?php

require_once 'app/Mage.php';

Mage::app('admin');

$collection = Mage::getResourceModel('catalog/product_collection')
    ->addAttributeToSelect('*')                                                                                                                                                                                                                                                 
    ->addFieldToFilter('entity_id', array('eq' => 231))
    ->setStore(2);

var_dump($collection->getFirstItem()->getName());

El resultado es el esperado:

string(18) "But I Am Le French"

editar:

No importa, el catálogo plano está específicamente prohibido para la tienda de administración:

// Flat Data can be used only on frontend
if (Mage::app()->getStore()->isAdmin()) {
    return false;
}

Investigando ...

edit2:

Parece que tienes razón. _initSelectse llama antes de que podamos modificar el storeId que se usa para generar el nombre de la tabla.

Por supuesto (si no queremos seguir la ruta de reescritura) podemos:

  • getSelect(), reiniciar y establecer un nuevo desde ()
  • $collection->getEntity()->setStoreId(123)y luego usa la reflexión para _initSelectvolver a llamar
  • Simplemente cree nuestro propio modelo de recursos y extiéndalo desde plano, proporcione alguna forma de insertar storeId en el momento adecuado ( __construct, retrasos _initSelect, etc.).
  • llama setCurrentStorecada vez que creamos la colección.

Pero todo esto se siente muy hacky ... Lo siento, esta puede ser una respuesta insatisfactoria :-(

edit3:

Entonces, en aras de proporcionar al menos una respuesta:

// Get collection and update store ID.
$collection = Mage::getResourceModel('catalog/product_collection');
$collection->getEntity()->setStoreId(2);

// Reset the select.
$collection->getSelect()->reset();

// Update table name.
$reflectionMethod = new ReflectionMethod($collection, '_initSelect');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke($collection);

// Do any other operations on the collection now.
$collection->addAttributeToSelect('*');

Por favor no uses eso ;-)

Daniel Sloof
fuente
Entonces, ¿también crees que es un error?
user487772
1
Leí el código, pero product_collectionel constructor acepta un modelo de recurso como argumento. Entonces, si crea un Product_Resource_Flat, establezca su id de tienda, clónelo y establezca un id de tienda diferente, luego páselo al constructor de la colección, ¿sería factible?
Melvyn
1
@Tim: Lo siento, solo acabo de ver tu comentario. Sí, creo que es un error.
Daniel Sloof
para una gran respuesta, está funcionando para 1.14.2.0
user4531
10

Así que considero que estos son dos errores en Magento.

El primero es el hecho de que no puede establecer la identificación de la tienda en la catalog/productcolección. Y el segundo es que absolutamente no puede obtener el modelo de recurso como no singleton.

Entonces, una solución estúpida es instanciar el modelo dos veces. La primera vez que se puede configurar el ID de la tienda y la segunda instancia lo usará:

Mage::getResourceModel('catalog/product_collection')->setStore($storeId);

$collection = Mage::getResourceModel('catalog/product_collection')
usuario487772
fuente
No sé por qué mi set store en Mage :: getModel ('catalog / category') -> getProductCollection () -> setStoreId () no funcionó y el tuyo funcionó. por cierto gracias
Nickool
3

Curiosamente, la tabla plana usada se establece una vez y nunca cambia, lo que funciona para EAV ya que el nombre de la tabla no cambia, pero no para plana, ya que el nombre de la tabla incluye el ID de la tienda. Una solución alternativa sería hacer un ayudante que intercambie la tabla en la parte DE de la consulta. Aquí hay un ejemplo de tal ayuda:

class My_Module_Helper_Data extends Mage_Core_Helper_Abstract
{
    public function getProductCollectionForStore($store)
    {
        $collection = Mage::getResourceModel('catalog/product_collection');

        // Change the store on the entity
        // This doesn't change it in the (already constructed) SQL query
        $collection->setStore($store);

        if (! $collection->isEnabledFlat()) {
            return $collection;
        }

        // Change the used table to the $store we want
        $select = $collection->getSelect();
        $from = $select->getPart('from');

        // Here, getFlatTableName() will pick up the store set above
        $from[$collection::MAIN_TABLE_ALIAS]['table'] =
        $from[$collection::MAIN_TABLE_ALIAS]['tableName'] = 
            $collection->getEntity()->getFlatTableName();

        $select->setPart('from', $from);
        return $collection;
    }
}

Entonces puedes usarlo simplemente con:

$collection = Mage::helper('my_module')->getProductCollectionForStore('somestore')
    ->addAttributeToSelect('name');

Me imagino que esto no causaría problemas para el SQL ya que está obteniendo todos los datos de una sola tabla plana, pero como es un singleton, la última tienda utilizada se usaría en cualquier otro lugar.

Una solución alternativa sería hacer un observador catalog_product_collection_load_beforeque haga algo como esto:

class My_Module_Model_Observer
{
    public function setCorrectFlatStore(Varien_Event_Observer $observer)
    {
        $collection = $observer->getCollection();
        if (! $collection->isEnabledFlat()) {
            return;
        }

        $select = $collection->getSelect();
        $from = $select->getPart('from');

        // If somebody called setStore() on the collection make sure
        // to update the used flat table
        $from[$collection::MAIN_TABLE_ALIAS]['table'] =
        $from[$collection::MAIN_TABLE_ALIAS]['tableName'] =
            $collection->getEntity()->getFlatTableName();

        $select->setPart('from', $from);
    }
}

Estoy de acuerdo en que los chicos de Magento deberían arreglar esto en el _beforeLoad()método.

adioe3
fuente
0

¿Por qué no usar un filtro habitual?

$collection->addAttributeToFilter('store_id', $store_id);

store_id se proporciona como una columna regular en la tabla * _eav_entity , por lo que también puede filtrar por ella. Trabajó para mi.

nakajuice
fuente
0

Be my works esta solución con core / app_emulation:

$storeId = 3;
$emulationModel = Mage::getModel('core/app_emulation');

// Emulate shop environment to disable using flat model and get collection for specific store
$emulationModel->startEnvironmentEmulation($storeId);
$products = Mage::getModel('catalog/product')->getCollection();
tomik
fuente