Як встановити ідентифікатор магазину в Mage_Catalog_Model_Resource_Product_Collection?


34

Завдання тривіальне. Мені потрібно отримати список товарів для конкретного перегляду магазину із включеним плоским каталогом. Найбільш очевидним рішенням є наступне:

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

Насправді setStore()метод не має жодних змін тут, оскільки він називається після того, як _initSelect()метод Mage_Catalog_Model_Resource_Product_Collectionотримує назву плоскої таблиці на основі ідентифікатора магазину. Оскільки ідентифікатор магазину ще не встановлений, він приймає поточний ідентифікатор магазину.

Таким очевидним вирішенням буде встановлення поточного ідентифікатора магазину перед тим, як отримати модель.

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

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

Це спрацює. Але тільки якщо вам потрібно отримати колекцію один раз. Якщо вам потрібно отримати колекцію в циклі або вам просто потрібні дві колекції "назад до спини", ви не зможете встановити для них певний магазин.

Причина полягає в тому, що Mage_Catalog_Model_Resource_Product_Flatклас має власну _storeIdвластивість, і в конструкторі він встановлений для поточного ідентифікатора магазину. Ось чому це буде встановлено вперше. Тоді чомусь (небо знає, я сподіваюся, що є) у Mage_Eav_Model_Entity_Collection_Abstract::_initкожному ресурсному модулі дістається як синглтон. Тож жоден конструктор для 2-го виклику.

Це все виглядає так неправильно, що я майже впевнений, що я помиляюся, і це не черговий помилка Magento (або два). Сподіваюся, хтось може пролити світло на це.


Чи потрібно використовувати getResourceModel (), оскільки це дає вам примірник? getModel ('каталог / resource_product_collection') може просто працювати.
Крістоф у Фомані

Ні, це абсолютно те саме. Це будь-яка інстанція моделі ресурсу одиночної будь-якої форми.
user487772

Тіме, додай це як відповідь, будь ласка!
Fabian Blechschmidt

@FabianBlechschmidt зроблено.
user487772

Відповіді:


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

Розслідування ...

edit2:

Схоже, ти маєш рацію. _initSelectвикликається, перш ніж ми зможемо змінити storeId, який використовується для генерування імені таблиці.

Звичайно (якщо ми не хочемо йти маршрутом перезапису), ми можемо:

  • getSelect(), зробіть скидання та встановіть нове з ()
  • $collection->getEntity()->setStoreId(123)а потім використати роздуми, щоб _initSelectзнову зателефонувати
  • Просто створіть нашу власну модель ресурсів і продовжте її від плоскої, дайте певний спосіб вставити storeId в потрібний час ( __construct, затягнувшись _initSelectтощо).
  • телефонуйте setCurrentStoreщоразу, коли ми створюємо колекцію.

Але всі вони дуже хакітні ... Вибачте, це може бути незадовільною відповіддю :-(

edit3:

Таким чином , для забезпечення , щонайменше , в відповідь:

// 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

1
Я переглянув код, але product_collectionконструктор приймає модель аргументу як аргумент. Отже, якщо ви створили Product_Resource_Flat, встановили його ідентифікатор магазину, клонували його та встановили інший ідентифікатор магазину, а потім передати його конструктору колекції, чи це буде можливо?
Мельвін

1
@Tim: Вибачте, лише побачив ваш коментар. Так, я думаю, що це помилка.
Даніель Слоуф

для чудової відповіді, він працює для 1.14.2.0
user4531

10

Тому я вважаю це двома помилками в Magento.

Перший - факт, що ви не можете встановити ідентифікатор магазину на catalog/productколекцію. А друге - це те, що ви абсолютно не можете отримати модель ресурсу як недістон.

Тож дурне вирішення - це інстанціювати модель двічі. Перший раз, коли можна встановити ідентифікатор магазину, а друга інстанція використовуватиме його:

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

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

Я не знаю, чому мій магазин наборів у Mage :: getModel ('каталог / категорія') -> getProductCollection () -> setStoreId () не працював, а ваш працював. до речі дякую
Nickool

3

Цікаво, що використана плоска таблиця встановлюється один раз і ніколи не змінюється, що працює для EAV, оскільки назва таблиці не змінюється, але не є плоскою, оскільки назва таблиці містить ідентифікатор магазину. Вирішення проблеми полягає в тому, щоб зробити помічника, який би поміняв таблицю ВІД частини запиту. Ось приклад такого помічника:

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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.