Jak ustawić identyfikator sklepu na Mage_Catalog_Model_Resource_Product_Collection?

34

Zadanie jest trywialne. Potrzebuję uzyskać listę produktów dla konkretnego widoku sklepu z włączonym płaskim katalogiem. Najbardziej oczywistym rozwiązaniem jest:

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

W rzeczywistości setStore()metoda nie robi tu żadnej różnicy, ponieważ jest wywoływana po _initSelect()metodzie, Mage_Catalog_Model_Resource_Product_Collectionktóra otrzymuje nazwę płaskiego stołu na podstawie identyfikatora sklepu. Ponieważ identyfikator sklepu nie jest jeszcze ustawiony, pobiera bieżący identyfikator sklepu.

Oczywistym obejściem byłoby ustawienie bieżącego identyfikatora sklepu przed uzyskaniem modelu.

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

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

To będzie działać. Ale tylko wtedy, gdy musisz raz zdobyć kolekcję. Jeśli potrzebujesz uzyskać kolekcję w pętli lub potrzebujesz tylko dwóch kolekcji z powrotem do tyłu, nie będziesz w stanie ustawić dla nich konkretnego sklepu.

Powodem jest to, że Mage_Catalog_Model_Resource_Product_Flatklasa ma swoją _storeIdwłasność, aw konstruktorze jest ustawiona na bieżący identyfikator sklepu. Dlatego zostanie ustawiony po raz pierwszy. Następnie z jakiegoś powodu (niebo wie, mam nadzieję, że istnieje) w Mage_Eav_Model_Entity_Collection_Abstract::_initkażdym module zasobów jest pobierany jako singleton. Więc nie ma konstruktora dla drugiego połączenia.

To wszystko wygląda tak źle, że jestem prawie pewien, że się mylę i nie jest to kolejny błąd Magento (lub dwa). Mam nadzieję, że ktoś może rzucić na to światło.

użytkownik487772
źródło
Czy musisz użyć getResourceModel (), ponieważ daje to instancję? getModel ('catalog / resource_product_collection') może po prostu działać.
Kristof at Fooman
Nie, to absolutnie to samo. Jest to tworzenie instancji pojedynczego modelu zasobów w jakikolwiek sposób.
user487772,
Tim, dodaj to jako odpowiedź, proszę!
Fabian Blechschmidt
@FabianBlechschmidt gotowe.
user487772,

Odpowiedzi:

13

Jaka to wersja Magento? Oto moje wyniki dla Magento 1.9:

Włączono płaski katalog:

Katalog płaski jest indeksowany:

Niektóre dane w określonym widoku sklepu:

Zastosowany kod:

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

Wynik jest zgodny z oczekiwaniami:

string(18) "But I Am Le French"

edytować:

Nieważne, płaski katalog jest szczególnie zabroniony dla sklepu administracyjnego:

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

Badanie ...

edycja2:

Wygląda na to, że masz rację. _initSelectjest wywoływany, zanim będziemy mogli zmodyfikować storeId, który jest używany do generowania nazwy tabeli.

Oczywiście (jeśli nie chcemy skorzystać z przepisanej trasy) możemy:

  • getSelect(), zresetuj i ustaw nowy from ()
  • $collection->getEntity()->setStoreId(123)a następnie użyj refleksji, aby _initSelectponownie zadzwonić
  • Po prostu stwórz nasz własny model zasobów i wyjdź z płaskiego, daj jakiś sposób na wstawienie storeId we właściwym czasie ( __constructopóźnienie _initSelectitp.).
  • dzwoń setCurrentStoreza każdym razem, gdy tworzymy kolekcję.

Ale wszyscy czują się bardzo hacky ... Niestety, może to być niezadowalająca odpowiedź :-(

edycja3:

Tak przez wzgląd na zapewnienie co najmniej o odpowiedź:

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

Nie używaj tego ;-)

Daniel Sloof
źródło
Czy uważasz, że to także błąd?
user487772,
1
Przejrzałem kod, ale product_collectionkonstruktor akceptuje model zasobów jako argument. Więc jeśli utworzysz Product_Resource_Flat, ustawisz identyfikator sklepu, sklonujesz go i ustawisz inny identyfikator sklepu, a następnie przekażesz go konstruktorowi kolekcji, czy byłoby to wykonalne?
Melvyn
1
@Tim: Przepraszamy, tylko zobaczyłem twój komentarz. Tak, myślę, że to błąd.
Daniel Sloof
świetna odpowiedź, działa dla 1.14.2.0
użytkownik4531
10

Uważam więc, że są to dwa błędy w Magento.

Pierwszym z nich jest fakt, że nie można ustawić identyfikatora sklepu na catalog/productkolekcji. Po drugie, absolutnie nie można uzyskać modelu zasobów jako nie singletonu.

Głupie obejście polega na dwukrotnym utworzeniu modelu. Po raz pierwszy można ustawić identyfikator sklepu i użyje go druga instancja:

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

$collection = Mage::getResourceModel('catalog/product_collection')
użytkownik487772
źródło
Nie wiem, dlaczego mój sklep z setami w Mage :: getModel ('katalog / kategoria') -> getProductCollection () -> setStoreId () nie działał, a twój działał. przy okazji dziękuję
Nickool,
3

Co ciekawe, używana płaska tabela jest ustawiana raz i nigdy się nie zmienia, co działa dla EAV, ponieważ nazwa tabeli się nie zmienia, ale nie dla płaskiej, ponieważ nazwa tabeli zawiera identyfikator sklepu. Obejściem byłoby utworzenie pomocnika, który zamieniłby tabelę w części FROM zapytania. Oto przykład takiego pomocnika:

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

Następnie możesz użyć go po prostu z:

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

Wyobrażam sobie, że nie spowodowałoby to problemów dla SQL, ponieważ pobierasz wszystkie dane z pojedynczej płaskiej tabeli, ale ponieważ jest to singleton, ostatnio używany sklep byłby używany wszędzie indziej.

Alternatywnym rozwiązaniem byłoby stworzenie obserwatora, catalog_product_collection_load_beforektóry zrobiłby coś takiego:

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

Zgadzam się, że faceci Magento powinni to naprawić w _beforeLoad()metodzie.

adioe3
źródło
0

Dlaczego nie użyć zwykłego filtra?

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

store_id jest podany jako zwykła kolumna w tabeli * _eav_entity , więc możesz go również filtrować. Pracował dla mnie.

nakajuice
źródło
0

Bądź moim dziełem to rozwiązanie z 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
źródło