Najlepsza praktyka łączenia atrybutów produktu

11

Mam niestandardową tabelę z odniesieniem do produktu product_id. Teraz chciałbym wyświetlić informacje o produkcie (SKU, nazwa) w siatce wewnętrznej bazy danych , ale nie jestem pewien, jaka jest najlepsza praktyka, aby to zrobić?

Moje najlepsze przypuszczenie SKUbrzmi następująco:

$collection->join(
    'catalog/product',
    'product_id=`catalog/product`.entity_id',
    array('product_sku' => 'sku')
)

(kod z _prepareCollection() metody w mojej klasie bloków grid)

Ale co z nazwą produktu? Można go znaleźć w catalog_product_entity_varchar. Rozumiem, że możesz raczej łatwo go zdobyć, jeśli twój własny model zasobów i kolekcja jest oparta, Mage_Eav_Model_Entity_Collection_Abstractponieważ wtedy możesz użyć metod takich jak joinAttribute. Ale mój model opiera się na prostej tabeli i jest rozszerzany Mage_Core_Model_Resource_Db_Collection_Abstracti nie ma joinAttributedostępnej metody.

Więc jaki jest najlepszy sposób na uzyskanie nazwy produktu w tym przypadku?

Dziękuję za poświęcony czas i pomoc :-)

Aktualizacja: Mówiąc ściślej, mówiłem o moim modelu zasobów i kolekcji. Pasuje do prostego płaskiego stołu z zaledwie kilkoma atrybutami

entity_id    product_id    created_at    user_id

Moim zamiarem jest gridowanie w backendie, gdzie pokazuję statystyki:

ProductSku    Count(ProductSku)    MAX(created_at)

O ile mi wiadomo, najlepszym podejściem do tego jest klasa bloków grid, a metodą jest _prepareCollection().

Moja metoda wygląda następująco:

protected function _prepareCollection()
{
    // Get and set our collection for the grid
    $collection = Mage::getResourceModel($this->_getCollectionClass());
    $collection
        ->join(
            'catalog/product',
            'product_id=`catalog/product`.entity_id',
            array('product_sku' => 'sku')
            )
        ->addExpressionFieldToSelect('product_count', 'COUNT({{product_id}})', 'product_id')
        ->addExpressionFieldToSelect('newest', 'MAX({{created_at}})', array('created_at'=>'main_table.created_at'))
        ->getSelect()->group('product_id');
    $this->setCollection($collection);

    return parent::_prepareCollection();
}

Działa to dobrze dla SKU (które w _prepareColums()metodzie nazywam product_sku . Ale co joinmuszę tu wstawić, aby uzyskać nazwę (i np. Producenta)?

Czy robię coś źle, ponieważ nie mogę użyć joinLeft()?

Celldweller
źródło

Odpowiedzi:

13

W swojej klasie kolekcji ( /Some/Module/Model/Mysql4 (or Resource)/YourModel/Collection.php) dodaj tę metodę:

public function addProductData()
    {
        /** add particular attribute code to this array */
        $productAttributes = array('name', 'price', 'url_key');
        foreach ($productAttributes as $attributeCode) {
            $alias     = $attributeCode . '_table';
            $attribute = Mage::getSingleton('eav/config')
                ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);

            /** Adding eav attribute value */
            $this->getSelect()->join(
                array($alias => $attribute->getBackendTable()),
                "main_table.product_id = $alias.entity_id AND $alias.attribute_id={$attribute->getId()}",
                array($attributeCode => 'value')
            );
            $this->_map['fields'][$attributeCode] = 'value';
        }
        /** adding catalog_product_entity table fields */
        $this->join(
            'catalog/product',
            'product_id=`catalog/product`.entity_id',
            array('sku' => 'sku', 'type_id' => 'type_id')
        );
        $this->_map['fields']['sku']     = 'sku';
        $this->_map['fields']['type_id'] = 'type_id';
        return $this;
    }

W swoim bloku siatki użyj tej funkcji:

 protected function _prepareCollection()
    {
        $collection = Mage::getModel('some/yourmodel')
            ->getCollection()->addProductData();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
mageUz
źródło
Wygląda to obiecująco, ale niestety nie działa. Moja kolekcja się rozszerza Mage_Core_Model_Resource_Db_Collection_Abstracti pojawia się błąd Call to undefined method Mycompany_Module_Model_Resource_Mymodel_Collection::joinLeft(). Chyba dlatego, że nie używam modelu zasobów EAV?
Celldweller
Użyj join zamiast joinLeft, zredagowałem odpowiedź
mageUz
doh! Czuję się teraz głupio! ;-) I nie wiem, dlaczego tego spróbowałem. Dziękuję Ci bardzo!
Celldweller
@Celldweller, $this->_map['fields']['sku'] = 'sku'w tym przypadku nie musisz nawet używać lub podobnie. Potrzebujesz go tylko wtedy, gdy masz kilka identycznych nazw pól i musisz wykonać tłumaczenie, aby zapobiec konfliktom. Po prostu dodaje narzut dla tego przypadku użycia. W innym przypadku użycia możesz użyć, $this->_map['fields']['my_sku'] = 'sku'a następnie możesz użyć z kolekcją i _prepareColumns: $this->addColumn('my_sku', array(…))pomoże to uniknąć konfliktu podczas filtrowania lub sortowania kolumn, jeśli masz pole skuużywane w różnych tabelach DB wspólnych dla kolekcji
Sylvain Rayé
Świetna odpowiedź! Może to jednak prowadzić do problemów z „produktem o tym samym identyfikatorze już istnieje”. Prosty $this->getSelect()->group('main_table.product_id');rozwiązuje problem, ale może kod można zoptymalizować, aby duplikaty nie były tworzone w pierwszej kolejności?
Simon
4

Cześć Celldweller Mam nadzieję, że masz się dobrze :-)

Być może myliłeś się w wyjaśnieniach na temat klasy Mage_Core_Model_Resource_Db_Collection_Abstract, rozszerzasz kolekcję zasobów, a nie model, ponieważ nie możesz rozszerzać modelu o klasę kolekcji, jeśli chcesz uszanować strukturę Magento. Czy mam rację?

Na podstawie mojej korekty widzę różne podejście, w zależności od tego, jak często chcesz uzyskać atrybut nazwy produktu. W każdym razie myślę, że wykonanie zapytania SQL za pomocą Magento Framework jest najlepszym sposobem i wydajne. Jest to szybsze niż wykonanie Mage::getModel('catalog/product')->load(1234)->getName()dla każdego załadowanego elementu. W rzeczywistości będzie bardzo podobny do kodu używanego dosku

KOD NIE TESTOWANY

Chcesz te informacje za każdym razem, gdy ładowana jest kolekcja

Możesz w swojej klasie kolekcji ustawić na _beforeLoadmetodę taki kod:

protected function _beforeLoad()
{
    $productName = Mage::getSingleton('eav/config')->getAttribute('catalog_product','name');

    $this->getSelect()
        ->join( array('product_attribute' => $productName->getBackendTable()),
            'main_table.product_id = product_attribute.entity_id',
            array())
        ->where("product_attribute.attribute_id = ?", $productName->getId());
    }

    return parent::_beforeLoad();
}

Chcesz te informacje TYLKO dla siatki

W twoim _prepareCollectionbędziesz musiał dodać metodę do swojej kolekcji z tym samym kodem, jak to zostało zrobione powyżej, a _beforeLoadnastępnie możesz przygotować kolekcję za pomocą tej metody. Nie używaj obu, to znaczy nie używaj razem tego samego kodu _beforeLoadi addProductNamemetod, użyj tylko jednego z nich. Oto próbka:

W twoje grid.php:

protected function _prepareCollection()
{
    ...
    $collection->addProductName();
    $this->setCollection($collection);
    return parent::_prepareCollection();
}

Do twojej kolekcji.php:

public function addProductName()
{
    $productName = Mage::getSingleton('eav/config')->getAttribute('catalog_product','name');

    $this->getSelect()
        -> join( array('product_attribute' => $productName->getBackendTable()),
            'main_table.product_id = product_attribute.entity_id',
            array())
        ->where("product_attribute.attribute_id = ?", $productName->getId());

    return $this;
}
Sylvain Rayé
źródło
Świetnie cię znowu przeczytać :-) Dziękuję bardzo, ale nie mogę użyć metody joinLeft (). Myślę, że jest to metoda oparta tylko na modelu zasobów EAV? Miałeś rację, moje wstępne pytanie było nieprecyzyjne.
Zmienię
Aha i btw: Oczywiście nie chcę robić produktu-> load () w takiej sytuacji. Zastrzel mnie, jeśli kiedykolwiek bym się odważył! ;-) Moim celem jest mieć to wszystko w jednej kolekcji, aby móc sortować i filtrować w siatce. Zobacz aktualizację w moim pierwszym poście, aby uzyskać więcej informacji.
Celldweller
Zastąp więc joinLeft przez join, ale już zaakceptowałeś inną odpowiedź, chociaż odpowiedziałem poprawnie i jako pierwszy odpowiedziałem :-(
Sylvain Rayé
Tak masz rację. Byłem tak szczęśliwy, że w końcu zadziałało, że o tym nie pomyślałem. Jestem wam bardzo wdzięczny. Mam nadzieję, że możesz mi wybaczyć! Jestem ci winien piwo.
Celldweller
Ach, nie martw się. Z przyjemnością spotykamy się w Berlinie :-)
Sylvain Rayé
4

Miałem prawie ten sam problem, ale nie mogę dodać komentarza, ponieważ nie mam 50 reputacji. Spędziłem dużo czasu próbując dowiedzieć się, co jest nie tak (użyłem kodu Sylvaina Rayé). Moja kolekcja produktów z jakiegoś powodu została odfiltrowana. Więc znalazłem powód.

Jeśli używasz niektórych narzędzi do importowania (magmi itp.), Często nie tworzą one jednocześnie pustych atrybutów. Dlatego używanie ->where("product_attribute.attribute_id = ?", $productName->getId())produktów, które nie mają tego atrybutu, zniknie z wyboru.

Właściwy sposób polega na użyciu w joinLeftten sposób:

$productName = Mage::getSingleton('eav/config')->getAttribute('catalog_product','name');

$this->getSelect()
     ->joinLeft(array('product_attribute' => $productName->getBackendTable()),
        "main_table.product_id = product_attribute.entity_id AND
         product_attribute.attribute_id = {$productName->getId()}",
        array());

Mam nadzieję, że to komuś pomoże.

spiil
źródło
-2

Wyświetl niestandardowy atrybut w siatce produktu.

Zastąp ten blok Mage_Adminhtml_Block_Catalog_Product_Grid w swoim rozszerzeniu i skopiuj funkcje _prepareCollection i _prepareColumns do pliku bloku rozszerzenia.

Dodaj poniższy kod, aby wybrać atrybut w _prepareCollection funkcji siatki produktu Mage_Adminhtml_Block_Catalog_Product_Grid przed wierszem $ this-> setCollection ($ collection).

$attributeCode = 'qc_status';//here your attribute code
        $collection->joinAttribute($attributeCode, 'catalog_product/'.$attributeCode, 'entity_id', null, 'left');
        $collection->addAttributeToSelect($attributeCode);

A następnie poniżej kodu dla kolumny w _prepareColumns funkcji siatki.

$attributeCodeConfig ='qc_status';//Your attribute code...

        $attributeId = Mage::getResourceModel('eav/entity_attribute')->getIdByCode('catalog_product', $attributeCodeConfig);

        $attribute = Mage::getModel('catalog/resource_eav_attribute')->load($attributeId);
        $attributeData = $attribute->getData();
        $frontEndLabel = $attributeData['frontend_label'];

        $attributeOptions = $attribute->getSource()->getAllOptions();
        $b = new Mage_Catalog_Model_Resource_Eav_Attribute();
        $attributeOptions2 = array();
        foreach ($attributeOptions as $value) {
            if(!empty($value['value'])) {
                $attributeOptions2[$value['value']] = $value['label'];
            }

        }


        if(count($attributeOptions2) > 0) {
            $this->addColumn($attributeCodeConfig,
                array(
                    'header'=> Mage::helper('catalog')->__($frontEndLabel),
                    'width' => '80px',
                    'index' => $attributeCodeConfig,
                    'type'  => 'options',
                    'options' => $attributeOptions2,

            ));
        } else {
            $this->addColumn($attributeCodeConfig,
                array(
                    'header'=> Mage::helper('catalog')->__($frontEndLabel),
                    'width' => '80px',
                    'index' => $attributeCodeConfig,

            ));
        }
Savoo
źródło
Zmiana podstawowych plików nie jest dobrą praktyką. To samo dotyczynew SomeModel()
sv3n
nie musimy zmieniać pliku podstawowego. możemy nadpisać ten blok (Mage_Adminhtml_Block_Catalog_Product_Grid), aby osiągnąć naszą funkcjonalność
Savoo