Лучшая практика для объединения атрибутов продукта

11

У меня есть пользовательская таблица со ссылкой на продукт product_id. Теперь я хотел бы показать информацию о продукте (sku, name) в своей сетке бэкэнда , но я не уверен, что лучше всего делать это?

Моя лучшая догадка SKUзаключается в следующем:

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

(код из _prepareCollection() метода в моем классе блока сетки)

Но как насчет названия продукта? Его можно найти в catalog_product_entity_varchar. Насколько я понимаю, вы можете легко получить его, если ваша собственная модель ресурсов и коллекция основаны на том, Mage_Eav_Model_Entity_Collection_Abstractчто вы можете использовать такие методы, как joinAttribute. Но моя модель основана на простой таблице и расширяется, Mage_Core_Model_Resource_Db_Collection_Abstractи нет joinAttributeдоступных методов.

Так каков наилучший способ получить название продукта в этом случае?

Спасибо за ваше время и помощь :-)

Обновление: если быть более точным, я говорил о моей модели ресурсов и коллекции. Он соответствует простой плоской таблице с несколькими атрибутами, такими как

entity_id    product_id    created_at    user_id

Мое намерение состоит в том, чтобы сделать сетку в бэкэнде, где я показываю некоторую статистику:

ProductSku    Count(ProductSku)    MAX(created_at)

Насколько я знаю, лучший способ сделать это - использовать класс блока сетки и метод _prepareCollection().

Мой метод выглядит так:

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

Это хорошо работает для sku (который я называю product_sku в _prepareColums()методе. Но что joinмне нужно вставить здесь, чтобы получить имя (и, например, производителя)?

Я делаю что-то не так, потому что я не могу использовать joinLeft()?

Celldweller
источник

Ответы:

13

В вашем классе коллекции ( /Some/Module/Model/Mysql4 (or Resource)/YourModel/Collection.php) добавьте этот метод:

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

В вашем блоке сетки используйте эту функцию:

 protected function _prepareCollection()
    {
        $collection = Mage::getModel('some/yourmodel')
            ->getCollection()->addProductData();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
mageUz
источник
Это выглядит многообещающе, но, к сожалению, не работает. Моя коллекция расширяется, Mage_Core_Model_Resource_Db_Collection_Abstractи я получаю ошибку Call to undefined method Mycompany_Module_Model_Resource_Mymodel_Collection::joinLeft(). Я думаю, это потому, что я не использую модель ресурсов EAV?
Celldweller
Используйте join вместо joinLeft, я отредактировал ответ
mageUz 31.10.13
DOH! Я чувствую себя глупо сейчас! ;-) И я не знаю, почему я попробовал это. Большое спасибо!
Celldweller
@ Celldweller, вам даже не обязательно использовать $this->_map['fields']['sku'] = 'sku'или подобное в этом случае. Это нужно только в том случае, если у вас есть несколько одинаковых имен полей и вам нужно выполнить перевод, чтобы избежать конфликтов. Это просто добавление накладных расходов для этого варианта использования. В другом сценарии использования вы можете использовать $this->_map['fields']['my_sku'] = 'sku'то, что вы можете использовать с коллекцией, и _prepareColumns: $this->addColumn('my_sku', array(…))это поможет вам предотвратить конфликт при фильтрации или сортировке столбцов, если у вас есть поле, skuиспользуемое в разных таблицах БД, общих для коллекции
Sylvain Rayé
Отличный ответ! Это может привести к проблемам "элемент с таким же идентификатором уже существует". Простое $this->getSelect()->group('main_table.product_id');решает проблему, но, может быть, код можно оптимизировать, чтобы дубликаты не создавались в первую очередь?
Саймон
4

Привет Celldweller Я надеюсь, у тебя все хорошо :-)

Возможно, вы ошиблись в своем объяснении о классе Mage_Core_Model_Resource_Db_Collection_Abstract, вы расширяете коллекцию ресурсов, а не модель, потому что вы не должны расширять модель с помощью класса коллекции, если хотите соблюдать структуру Magento. Я прав?

Исходя из моей коррекции, я вижу разные подходы, в зависимости от того, как часто вы хотите получить атрибут названия продукта. В любом случае, я думаю, что сделать SQL-запрос через Magento Framework - это лучший и эффективный способ. Это быстрее, чем сделать Mage::getModel('catalog/product')->load(1234)->getName()для каждого загруженного элемента. Так что на самом деле это будет очень похоже на код, который вы используете дляsku

КОД НЕ ПРОВЕРЕН

Вы хотите эту информацию каждый раз, когда коллекция загружается

Вы можете в своем классе коллекции установить в _beforeLoad метод такой код:

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

Вы хотите эту информацию ТОЛЬКО для сетки

В вашей _prepareCollection, вам нужно будет добавить метод в вашей коллекции с тем же кодом, как это было сделано выше, в_beforeLoad затем вы могли подготовить коллекцию с помощью этого метода. Не используйте оба, я имею в виду, не используйте вместе один и тот же код _beforeLoadи addProductNameметоды, используйте только один из них. Вот образец:

В ваш grid.php:

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

В ваш Collection.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;
}
Сильвен Райе
источник
Приятно снова прочитать вас :-) Большое спасибо, но я не могу использовать метод joinLeft (). Я предполагаю, что это метод только для модели ресурсов EAV? Вы были правы, я был неточен в своем первоначальном вопросе. Я буду редактировать его :)
Celldweller
Да и кстати: конечно, я не хочу делать product-> load () в такой ситуации. Стреляй в меня, если я когда-нибудь посмел бы! ;-) Моя цель - собрать все это в одну коллекцию, чтобы можно было выполнять сортировку и фильтрацию в сетке. Смотрите обновление в моем первоначальном посте для получения дополнительной информации.
Celldweller
Поэтому замените joinLeft на join, но вы уже приняли другой ответ, хотя я ответил правильно и первым ответил :-(
Сильвен Райе
Да ты прав. Я был так счастлив, что это наконец сработало, что я не думал об этом. Я очень благодарен вам обоим. Надеюсь, вы можете простить меня! Я должен тебе пиво.
Celldweller
ахах не беспокойся Мы с удовольствием встретимся в Берлине :-)
Сильвен Райе
4

У меня была почти такая же проблема, но я не могу добавить комментарий, потому что у меня нет 50 репутации. Я потратил много времени, пытаясь выяснить, что не так (я использовал код Sylvain Rayé). Моя коллекция продуктов почему-то отфильтрована. Итак, я нашел причину.

Если вы используете некоторые инструменты импорта (magmi и т. Д.), Они часто не создают пустые атрибуты сразу. Поэтому использование ->where("product_attribute.attribute_id = ?", $productName->getId())товаров, не имеющих этого атрибута, исчезнет из выбора.

Правильный путь использует joinLeftвот так:

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

Надеюсь, это кому-нибудь поможет.

spiil
источник
-2

Отобразить пользовательский атрибут в сетке товара

Перезапишите этот блок Mage_Adminhtml_Block_Catalog_Product_Grid в своем расширении и скопируйте функции _prepareCollection и _prepareColumns в файл блока вашего расширения.

Добавьте приведенный ниже код для выбора атрибута в функции _prepareCollection сетки продуктов Mage_Adminhtml_Block_Catalog_Product_Grid перед строкой $ this-> setCollection ($ collection).

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

А затем ниже код для столбца в функции _prepareColumns сетки.

$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
источник
Изменение основных файлов не является хорошей практикой. То же самое дляnew SomeModel()
sv3n
нам не нужно вносить изменения в основной файл. Мы можем перезаписать этот блок (Mage_Adminhtml_Block_Catalog_Product_Grid), чтобы реализовать нашу функциональность
Savoo