Bagaimana cara menetapkan ID toko di Mage_Catalog_Model_Resource_Product_Collection?

34

Tugasnya sepele. Saya perlu mendapatkan daftar produk untuk tampilan toko tertentu dengan katalog datar diaktifkan. Solusi yang paling jelas adalah sebagai berikut:

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

Sebenarnya setStore()metode ini tidak membuat perbedaan di sini karena ini disebut setelah _initSelect()metode Mage_Catalog_Model_Resource_Product_Collectionyang mendapatkan nama tabel datar berdasarkan ID toko. Karena ID toko belum disetel, ID toko saat ini diperlukan.

Jadi solusi yang jelas adalah dengan menetapkan ID toko saat ini sebelum mendapatkan model.

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

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

Itu akan berhasil. Tetapi hanya jika Anda perlu mendapatkan koleksi sekali. Jika Anda perlu mendapatkan koleksi di loop atau Anda hanya perlu dua koleksi back to back Anda tidak akan dapat mengatur toko khusus untuk mereka.

Alasannya adalah bahwa Mage_Catalog_Model_Resource_Product_Flatkelas memiliki _storeIdproperti itu sendiri dan dalam konstruktor itu diatur ke ID toko saat ini. Itu sebabnya ini akan ditetapkan pertama kali. Kemudian untuk beberapa alasan (surga tahu saya harap ada satu) di Mage_Eav_Model_Entity_Collection_Abstract::_initsetiap modul sumber daya diambil sebagai singleton. Jadi tidak ada konstruktor untuk panggilan kedua.

Ini semua terlihat sangat salah sehingga saya cukup yakin saya salah dan itu bukan bug Magento lainnya (atau dua). Semoga seseorang bisa menjelaskannya.

pengguna487772
sumber
Apakah Anda harus menggunakan getResourceModel () karena ini memberi Anda contoh? getModel ('catalog / resource_product_collection') mungkin hanya berfungsi.
Kristof di Fooman
Tidak, ini sama saja. Ini adalah instantiating sumber daya model tunggal dengan cara apa pun.
user487772
Tim, tolong tambahkan itu sebagai jawaban!
Fabian Blechschmidt
@FabianBlechschmidt selesai.
user487772

Jawaban:

13

Magento versi apa ini? Ini adalah hasil saya untuk Magento 1.9:

Katalog datar diaktifkan:

Katalog datar diindeks:

Beberapa data diatur dalam tampilan toko tertentu:

Kode yang digunakan:

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

Hasilnya seperti yang diharapkan:

string(18) "But I Am Le French"

edit:

Nevermind, katalog flat secara khusus dilarang untuk toko admin:

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

Investigasi ...

edit2:

Sepertinya Anda benar. _initSelectdipanggil sebelum kita dapat memodifikasi storeId yang digunakan untuk menghasilkan nama tabel.

Tentu saja (jika kita tidak ingin melanjutkan rute penulisan ulang) kita dapat:

  • getSelect(), lakukan reset dan atur yang baru dari ()
  • $collection->getEntity()->setStoreId(123)dan kemudian gunakan refleksi untuk menelepon _initSelectlagi
  • Cukup buat model sumber daya kami sendiri dan lanjutkan dari flat, berikan beberapa cara memasukkan storeId pada waktu yang tepat ( __construct, menunda _initSelect, dll).
  • hubungi setCurrentStoresetiap kali kami membuat koleksi.

Tapi ini semua terasa sangat berantakan ... Maaf, ini mungkin jawaban yang tidak memuaskan :-(

edit3:

Jadi demi menyediakan setidaknya sebuah jawaban:

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

Tolong jangan gunakan itu ;-)

Daniel Sloof
sumber
Jadi apakah Anda juga berpikir itu adalah bug?
user487772
1
Saya membaca kode, tetapi product_collectionkonstruktor menerima model sumber daya sebagai argumen. Jadi jika Anda membuat Product_Resource_Flat, atur id toko itu, tiru dan atur id toko lain, lalu berikan ke konstruktor koleksi, apakah itu bisa dilakukan?
Melvyn
1
@Tim: Maaf, baru saja melihat komentar Anda. Ya saya pikir itu bug.
Daniel Sloof
untuk jawaban yang bagus, ini berfungsi untuk 1.14.2.0
user4531
10

Jadi saya menganggap ini sebagai dua bug di Magento.

Yang pertama adalah fakta bahwa Anda tidak dapat menetapkan ID toko pada catalog/productkoleksi. Dan yang kedua adalah Anda benar-benar tidak bisa mendapatkan model sumber daya sebagai non-tunggal.

Jadi solusi bodoh adalah dengan instantiate model dua kali. Pertama kali ID toko dapat disetel dan instantiasi kedua akan menggunakannya:

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

$collection = Mage::getResourceModel('catalog/product_collection')
pengguna487772
sumber
Saya tidak tahu mengapa toko set saya di Mage :: getModel ('katalog / kategori') -> getProductCollection () -> setStoreId () tidak berfungsi dan toko Anda berfungsi.
Ngomong
3

Menariknya, tabel datar yang digunakan diatur sekali dan tidak pernah berubah yang berfungsi untuk EAV karena nama tabel tidak berubah tetapi tidak untuk flat karena nama tabel menyertakan ID toko. Solusinya adalah membuat helper yang akan menukar tabel di bagian FROM dari kueri. Berikut ini contoh pembantu seperti itu:

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

Maka Anda dapat menggunakannya hanya dengan:

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

Saya membayangkan ini tidak akan menimbulkan masalah untuk SQL karena Anda mengambil semua data dari satu tabel datar tapi karena itu adalah singleton toko yang terakhir digunakan akan digunakan di tempat lain.

Solusi alternatif adalah dengan membuat pengamat catalog_product_collection_load_beforeyang melakukan sesuatu seperti ini:

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

Saya setuju bahwa orang-orang Magento harus memperbaikinya dalam _beforeLoad()metode ini.

adioe3
sumber
0

Mengapa tidak menggunakan filter biasa?

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

store_id diberikan sebagai kolom reguler di tabel * _eav_entity , jadi Anda juga bisa memfilternya. Bekerja untukku.

nakajuice
sumber
0

Jadilah karya saya solusi ini dengan 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
sumber