Wie lege ich eine Geschäfts-ID für Mage_Catalog_Model_Resource_Product_Collection fest?

34

Die Aufgabe ist trivial. Ich muss eine Liste der Produkte für eine bestimmte Store-Ansicht mit aktiviertem Flat-Katalog abrufen. Die naheliegendste Lösung ist die folgende:

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

Tatsächlich macht die setStore()Methode hier keinen Unterschied, da sie aufgerufen wird, nachdem die _initSelect()Methode Mage_Catalog_Model_Resource_Product_Collectionanhand der Geschäfts-ID den Namen der flachen Tabelle abgerufen hat. Da die Filial-ID noch nicht festgelegt ist, wird die aktuelle Filial-ID verwendet.

Die naheliegende Problemumgehung besteht darin, eine aktuelle Geschäfts-ID festzulegen, bevor Sie ein Modell abrufen.

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

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

Es wird klappen. Aber nur, wenn Sie einmal eine Sammlung benötigen. Wenn Sie eine Sammlung auf den neuesten Stand bringen möchten oder nur zwei aufeinanderfolgende Sammlungen benötigen, können Sie keinen bestimmten Speicher für diese festlegen.

Der Grund dafür ist, dass die Mage_Catalog_Model_Resource_Product_FlatKlasse eine eigene _storeIdEigenschaft hat und im Konstruktor auf die aktuelle Geschäfts-ID festgelegt ist. Deshalb wird es beim ersten Mal eingestellt. Dann wird aus irgendeinem Grund (der Himmel weiß, ich hoffe, es gibt einen) in Mage_Eav_Model_Entity_Collection_Abstract::_initjedem Ressourcenmodul als Singleton abgerufen. Also kein Konstruktor für den 2. Aufruf.

Das alles sieht so falsch aus, dass ich ziemlich sicher bin, dass ich falsch liege und es kein weiterer Magento-Bug (oder zwei) ist. Ich hoffe, jemand kann Licht ins Dunkel bringen.

user487772
quelle
Müssen Sie getResourceModel () verwenden, um die Instanz zu erhalten? getModel ('catalog / resource_product_collection') könnte einfach funktionieren.
Kristof bei Fooman
Nein, es ist absolut dasselbe. Es instanziiert in irgendeiner Weise das Ressourcenmodell Singleton.
User487772
Tim, füge es bitte als Antwort hinzu!
Fabian Blechschmidt
@FabianBlechschmidt erledigt.
User487772

Antworten:

13

Welche Version von Magento ist das? Dies sind meine Ergebnisse für Magento 1.9:

Flacher Katalog aktiviert:

Der flache Katalog ist indexiert:

Einige Datensätze in einer bestimmten Geschäftsansicht:

Code verwendet:

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

Ergebnis ist wie erwartet:

string(18) "But I Am Le French"

bearbeiten:

Egal, flacher Katalog ist speziell für den Admin Store verboten:

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

Untersuchung ...

edit2:

Sieht so aus, als hättest du recht. _initSelectwird aufgerufen, bevor die storeId geändert werden kann, mit der der Tabellenname generiert wird.

Natürlich können wir (wenn wir nicht den Weg des Umschreibens gehen wollen):

  • getSelect(), mache einen Reset und setze einen neuen von ()
  • $collection->getEntity()->setStoreId(123)und rufen Sie dann _initSelecterneut mit Reflection an
  • Erstellen Sie einfach ein eigenes Ressourcenmodell und gehen Sie von der Ebene aus, geben Sie eine Möglichkeit an, storeId zum richtigen Zeitpunkt einzufügen ( __construct, zu verzögern _initSelectusw.).
  • rufen Sie setCurrentStorejedes Mal an, wenn wir die Sammlung erstellen.

Aber diese fühlen sich alle sehr abgefahren an ... Sorry, das könnte eine unbefriedigende Antwort sein :-(

edit3:

Um zumindest eine Antwort zu geben:

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

Bitte nicht benutzen ;-)

Daniel Sloof
quelle
Denken Sie also auch, dass es sich um einen Fehler handelt?
User487772
1
Ich habe den Code überflogen, aber product_collectionder Konstruktor akzeptiert ein Ressourcenmodell als Argument. Wenn Sie also eine erstellen Product_Resource_Flat, ihre Geschäfts-ID festlegen, sie klonen und eine andere Geschäfts-ID festlegen und sie dann an den Auflistungskonstruktor übergeben, ist dies möglich?
Melvyn
1
@ Tim: Sorry, habe gerade deinen Kommentar gesehen. Ja, ich denke, es ist ein Fehler.
Daniel Sloof
up for great answer, es funktioniert für 1.14.2.0
user4531
10

Ich betrachte dies als zwei Fehler in Magento.

Erstens können Sie die Geschäfts-ID nicht für die catalog/productSammlung festlegen . Und das zweite ist, dass Sie als Nicht-Singleton absolut kein Ressourcenmodell erhalten können.

Es ist also eine blöde Lösung, das Modell zweimal zu instanziieren. Das erste Mal, wenn die Geschäfts-ID festgelegt werden kann, wird sie von der zweiten Instanz verwendet:

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

$collection = Mage::getResourceModel('catalog/product_collection')
user487772
quelle
Ich weiß nicht, warum mein Set-Store in Mage :: getModel ('catalog / category') -> getProductCollection () -> setStoreId () nicht funktioniert hat und deines funktioniert hat. übrigens danke
Nickool
3

Interessanterweise wird die verwendete flache Tabelle einmal festgelegt und nie geändert, was für EAV funktioniert, da sich der Tabellenname nicht ändert, aber nicht für die flache Tabelle, da der Tabellenname die Geschäfts-ID enthält. Eine Problemumgehung wäre, einen Helfer zu erstellen, der die Tabelle im FROM-Teil der Abfrage austauscht. Hier ist ein Beispiel für einen solchen Helfer:

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

Dann können Sie es einfach mit:

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

Ich stelle mir vor, dass dies für SQL keine Probleme bereiten würde, da Sie alle Daten von einer einzigen flachen Tabelle abrufen. Da es sich jedoch um einen Singleton handelt, wird der zuletzt verwendete Speicher überall anders verwendet.

Eine alternative Lösung wäre, einen Beobachter zu machen, catalog_product_collection_load_beforeder etwas in der Art macht:

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

Ich bin damit einverstanden, dass Magento-Leute dies in der _beforeLoad()Methode beheben sollten .

adioe3
quelle
0

Warum nicht einen gewöhnlichen Filter verwenden?

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

store_id wird als reguläre Spalte in der * _eav_entity- Tabelle angegeben, sodass Sie auch danach filtern können. Hat für mich gearbeitet.

Nakajuice
quelle
0

Sei mein Werk diese Lösung mit 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
quelle