如何在Mage_Catalog_Model_Resource_Product_Collection上设置商店ID?


34

这项任务是微不足道的。我需要获取启用了平面目录的特定商店视图的产品列表。最明显的解决方案如下:

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

实际上,setStore()方法在这里没有什么区别,因为方法是在_initSelect()方法Mage_Catalog_Model_Resource_Product_Collection基于商店ID获得平面表的名称之后调用的。由于尚未设置商店ID,因此它将采用当前商店ID。

因此,显而易见的解决方法是在获取模型之前设置当前商店ID。

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

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

会的。但仅当您需要一次收集一次时。如果您需要循环获取一个集合,或者只需要两个背对背集合,则将无法为其设置特定的商店。

原因是Mage_Catalog_Model_Resource_Product_Flat该类具有其自己的_storeId属性,并且在构造函数中将其设置为当前商店ID。这就是为什么它将被第一次设置。然后由于某种原因(天堂知道我希望有一个)在Mage_Eav_Model_Entity_Collection_Abstract::_init每个资源模块中被作为单例获取。因此,没有第二个调用的构造函数。

这一切看起来都非常错误,以至于我很确定我错了,这不是另一个(或两个)Magento错误。希望有人能对此有所启发。


您是否必须使用getResourceModel(),因为它为您提供了实例?getModel('catalog / resource_product_collection')可能会起作用。
Kristof在Fooman,2014年

不,绝对一样。它以任何方式实例化资源模型单例。
user487772 2014年

蒂姆,请添加为答案!
Fabian Blechschmidt,2014年

@FabianBlechschmidt完成。
user487772 2014年

Answers:


13

这是什么版本的Magento?这些是我对Magento 1.9的搜索结果:

平面目录已启用:

平面目录已建立索引:

特定商店视图中的一些数据集:

使用的代码:

<?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());

结果符合预期:

string(18) "But I Am Le French"

编辑:

没关系,管理员商店特别禁止使用平面目录:

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

正在调查...

编辑2:

看来您是对的。_initSelect在我们能够修改用于生成表名的storeId之前调用。

当然(如果我们不想走重写路线),我们可以:

  • getSelect(),进行重置并设置新的from()
  • $collection->getEntity()->setStoreId(123)然后使用反射来_initSelect再次调用
  • 只需创建我们自己的资源模型并从平面扩展,就可以在适当的时间提供一些插入storeId的方式(__construct,delaying _initSelect等)。
  • setCurrentStore每当我们创建集合时都会调用。

但这一切都让人感到很hacky ...对不起,这可能是一个不令人满意的答案:-(

编辑3:

因此,为了至少提供一个答案:

// 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('*');

请不要使用;-)


那么,您还认为这是一个错误吗?
user487772 2014年

1
我浏览了一下代码,但是product_collection的构造函数接受资源模型作为参数。因此,如果创建一个Product_Resource_Flat,将其设置为商店ID,将其克隆并设置一个不同的商店ID,然后将其传递给集合构造函数,这样做可行吗?
梅尔文2014年

1
@Tim:对不起,只看到您的评论。是的,我认为这是一个错误。
Daniel Sloof 2014年

很好的答案,它适用于1.14.2.0
user4531 2015年

10

因此,我认为这些是Magento中的两个错误。

第一个事实是您无法在catalog/product集合上设置商店ID 。其次,您绝对无法获得非单身的资源模型。

因此,愚蠢的解决方法是实例化模型两次。第一次可以设置商店ID,第二个实例将使用它:

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

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

我不知道为什么我在Mage :: getModel('catalog / category')-> getProductCollection()-> setStoreId()中的集合商店不起作用,而您的商店却起作用了。顺便说一句谢谢
Nickool 2014年

3

有趣的是,使用过的平面表只设置了一次就不会更改,这对于EAV来说是有效的,因为表名不会更改,而对于平面表则不会,因为表名包含商店ID。一种解决方法是制作一个辅助程序,以换出查询的FROM部分中的表。这是一个这样的助手的例子:

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

然后,您可以将其简单地用于:

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

我想这不会对SQL造成麻烦,因为您可以从单个平面表中获取所有数据,但是由于它是一个单例,因此最后使用的存储将在其他地方使用。

另一种解决方案是使观察者在catalog_product_collection_load_before其上执行以下操作:

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

我同意Magento家伙应该在_beforeLoad()方法中解决此问题。


0

为什么不使用通常的过滤器?

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

store_id是* _eav_entity表中的常规列,因此您也可以对其进行过滤。为我工作。


0

在我的作品中,这个带有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();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.