Como definir um ID de loja no Mage_Catalog_Model_Resource_Product_Collection?


34

A tarefa é trivial. Preciso obter uma lista de produtos para uma visualização específica da loja com um catálogo plano ativado. A solução mais óbvia é a seguinte:

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

De fato, o setStore()método não faz nenhuma diferença aqui porque é chamado após o _initSelect()método Mage_Catalog_Model_Resource_Product_Collectionque obtém o nome da tabela simples com base no ID da loja. Como o ID da loja ainda não está definido, ele aceita o ID da loja atual.

Portanto, a solução óbvia seria definir um ID de loja atual antes de obter um modelo.

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

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

Vai funcionar. Mas apenas se você precisar obter uma coleção uma vez. Se você precisar obter uma coleção no loop ou precisar apenas de duas coleções consecutivas, não poderá definir um armazenamento específico para elas.

O motivo é que a Mage_Catalog_Model_Resource_Product_Flatclasse tem sua própria _storeIdpropriedade e, no construtor, é definida como o ID da loja atual. É por isso que será definido pela primeira vez. Então, por alguma razão (Deus sabe que espero que exista), em Mage_Eav_Model_Entity_Collection_Abstract::_initcada módulo de recurso é buscado como um singleton. Portanto, nenhum construtor para a segunda chamada.

Tudo isso parece tão errado que eu tenho certeza que estou errado e não é outro bug do Magento (ou dois). Espero que alguém possa esclarecer isso.


Você precisa usar getResourceModel (), pois isso fornece a instância? getModel ('catalog / resource_product_collection') pode funcionar.
Kristof em Fooman

Não, é absolutamente o mesmo. Ele está instanciando o modelo de recurso singleton de qualquer maneira.
precisa saber é o seguinte

Tim, adicione-o como resposta, por favor!
Fabian Blechschmidt

@FabianBlechschmidt done.
precisa saber é o seguinte

Respostas:


13

Que versão do Magento é essa? Estes são os meus resultados para o Magento 1.9:

Catálogo plano ativado:

O catálogo plano é indexado:

Alguns conjuntos de dados em uma visualização de loja 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());

O resultado é o esperado:

string(18) "But I Am Le French"

editar:

Não importa, o catálogo plano é especificamente proibido para o repositório de administradores:

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

Investigando ...

edit2:

Parece que você está certo. _initSelecté chamado antes que possamos modificar o storeId que é usado para gerar o nome da tabela.

Obviamente (se não quisermos seguir a rota de reescrita), podemos:

  • getSelect(), redefina e defina um novo de ()
  • $collection->getEntity()->setStoreId(123)e depois use a reflexão para ligar _initSelectnovamente
  • Basta criar nosso próprio modelo de recursos e estender a partir do flat, dar uma maneira de inserir o storeId no momento certo ( __construct, atrasar _initSelect, etc.).
  • ligue setCurrentStoresempre que criarmos a coleção.

Mas tudo isso parece muito hacky ... Desculpe, isso pode ser uma resposta insatisfatória :-(

edit3:

Portanto, para fornecer pelo menos uma resposta:

// 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, não use isso ;-)


Então você também acha que é um bug?
precisa saber é o seguinte

11
Passei os olhos pelo código, mas product_collectiono construtor aceita um modelo de recurso como argumento. Portanto, se você criar um Product_Resource_Flat, defina seu ID de loja, clone-o e defina um ID de loja diferente e passe-o para o construtor de coleções, isso seria possível?
Melvyn

11
@ Tim: Desculpe, só vi o seu comentário. Sim, eu acho que é um bug.
precisa saber é o seguinte

para uma ótima resposta, ele está funcionando para 1.14.2.0
user4531

10

Então eu considero esses dois bugs no Magento.

O primeiro é o fato de que você não pode definir o ID da loja na catalog/productcoleção. E a segunda é que você absolutamente não pode obter o modelo de recurso como não singleton.

Uma solução tão estúpida é instanciar o modelo duas vezes. A primeira vez que o ID da loja pode ser definido e a segunda instanciação o utilizará:

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

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

Não sei por que meu set store no Mage :: getModel ('catalog / category') -> getProductCollection () -> setStoreId () não funcionou e o seu funcionou. pela maneira obrigado
Nickool

3

Curiosamente, a tabela simples usada é configurada uma vez e nunca é alterada, o que funciona para o EAV, pois o nome da tabela não muda, mas não para o flat, pois o nome da tabela inclui o ID da loja. Uma solução alternativa seria criar um auxiliar que trocasse a tabela na parte FROM da consulta. Aqui está um exemplo de tal ajudante:

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;
    }
}

Então você pode usá-lo simplesmente com:

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

Eu imagino que isso não causaria problemas para o SQL, pois você está buscando todos os dados de uma única tabela plana, mas como é um singleton, o último armazenamento usado seria usado em qualquer outro lugar.

Uma solução alternativa seria criar um observador catalog_product_collection_load_beforeque faça algo assim:

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);
    }
}

Concordo que os caras do Magento devem corrigir isso no _beforeLoad()método.


0

Por que não usar um filtro comum?

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

store_id é fornecido como uma coluna regular na tabela * _eav_entity , para que você também possa filtrar por ela. Trabalhou para mim.


0

Seja meus trabalhos esta solução com 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();
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.