Come impostare un ID negozio su Mage_Catalog_Model_Resource_Product_Collection?


34

Il compito è banale. Devo ottenere un elenco di prodotti per una particolare visualizzazione del negozio con un catalogo flat abilitato. La soluzione più ovvia è la seguente:

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

In effetti il setStore()metodo non fa alcuna differenza qui perché viene chiamato dopo il _initSelect()metodo di Mage_Catalog_Model_Resource_Product_Collectioncui ottiene il nome della tabella piatta in base all'ID negozio. Poiché l'ID negozio non è ancora impostato, accetta l'ID negozio corrente.

Quindi la soluzione ovvia sarebbe quella di impostare un ID negozio corrente prima di ottenere un modello.

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

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

Funzionerà. Ma solo se hai bisogno di ottenere una collezione una volta. Se hai bisogno di ottenere una collezione nel loop o hai solo bisogno di due collezioni back to back, non sarai in grado di impostare un negozio specifico per loro.

Il motivo è che la Mage_Catalog_Model_Resource_Product_Flatclasse ha una propria _storeIdproprietà e nel costruttore è impostata sull'ID negozio corrente. Ecco perché sarà impostato per la prima volta. Quindi per qualche motivo (il cielo sa che spero che ce ne sia uno) in Mage_Eav_Model_Entity_Collection_Abstract::_initogni modulo di risorsa viene recuperato come singleton. Quindi nessun costruttore per la seconda chiamata.

Tutto sembra così sbagliato che sono abbastanza sicuro di sbagliarmi e non è un altro bug di Magento (o due). Spero che qualcuno possa far luce su di esso.


Devi usare getResourceModel () in quanto ti dà l'istanza? getModel ('catalog / resource_product_collection') potrebbe funzionare.
Kristof a Fooman il

No, è assolutamente lo stesso. Sta istanziando singleton del modello di risorsa in alcun modo.
user487772,

Tim, aggiungilo come risposta per favore!
Fabian Blechschmidt,

@FabianBlechschmidt fatto.
user487772,

Risposte:


13

Quale versione di Magento è questa? Questi sono i miei risultati per Magento 1.9:

Catalogo flat abilitato:

Il catalogo flat è indicizzato:

Alcuni set di dati in una vista negozio specifica:

Codice utilizzato:

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

Il risultato è come previsto:

string(18) "But I Am Le French"

modificare:

Nevermind, il catalogo flat è specificamente proibito per il negozio di amministrazione:

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

Indagare ...

EDIT2:

Sembra che tu abbia ragione. _initSelectviene chiamato prima di poter modificare lo storeId che viene utilizzato per generare il nome della tabella.

Naturalmente (se non vogliamo seguire il percorso di riscrittura) possiamo:

  • getSelect(), fai un reset e imposta un nuovo da ()
  • $collection->getEntity()->setStoreId(123)e poi utilizzare la reflection per richiamare _initSelectdi nuovo
  • Basta creare il nostro modello di risorse ed estenderlo dal piano, fornire un modo per inserire storeId al momento giusto ( __construct, ritardare _initSelect, ecc.).
  • chiama setCurrentStoreogni volta che creiamo la raccolta.

Ma tutti questi sembrano molto confusi ... Mi dispiace, questa potrebbe essere una risposta insoddisfacente :-(

Edit3:

Quindi, per il bene di fornire almeno una risposta:

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

Per favore, non usarlo ;-)


Quindi pensi anche che sia un bug?
user487772,

1
Ho sfogliato il codice, ma product_collectionil costruttore accetta un modello di risorsa come argomento. Quindi se si crea un Product_Resource_Flat, si imposta l'ID negozio, si clona e si imposta un ID negozio diverso, quindi lo si passa al costruttore della raccolta, sarebbe fattibile?
Melvyn,

1
@Tim: Siamo spiacenti, ho appena visto il tuo commento. Sì, penso che sia un bug.
Daniel Sloof,

per un'ottima risposta, funziona per 1.14.2.0
user4531

10

Quindi considero questi due bug in Magento.

Il primo è il fatto che non è possibile impostare l'ID negozio sulla catalog/productraccolta. E il secondo è che non puoi assolutamente ottenere un modello di risorsa come non-singleton.

Una soluzione così stupida consiste nell'istanziare il modello due volte. La prima volta che è possibile impostare l'ID negozio e la seconda istanza lo utilizzerà:

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

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

Non so perché il mio negozio di set in Mage :: getModel ('catalog / category') -> getProductCollection () -> setStoreId () non ha funzionato e il tuo ha funzionato. a proposito grazie
Nickool

3

È interessante notare che la tabella flat utilizzata viene impostata una volta e non viene mai modificata, il che funziona per EAV poiché il nome della tabella non cambia ma non per flat poiché il nome della tabella include l'ID negozio. Una soluzione alternativa sarebbe quella di creare un helper che cambierebbe la tabella nella parte FROM della query. Ecco un esempio di tale aiuto:

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

Quindi puoi usarlo semplicemente con:

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

Immagino che questo non causerebbe alcun problema per l'SQL poiché stai recuperando tutti i dati da una singola tabella piatta, ma poiché si tratta di un singleton, l'ultimo archivio utilizzato verrebbe utilizzato ovunque.

Una soluzione alternativa sarebbe quella di fare un osservatore sul catalog_product_collection_load_beforequale fa qualcosa del genere:

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

Sono d'accordo che i ragazzi di Magento dovrebbero risolvere questo problema con il _beforeLoad()metodo.


0

Perché non usare un normale filtro?

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

store_id è indicato come una colonna normale nella tabella * _eav_entity , quindi puoi anche filtrarlo. Ha funzionato per me.


0

Sii il mio lavoro questa soluzione con 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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.