Prática recomendada para associar atributos do produto

11

Eu tenho uma tabela personalizada com uma referência de produto product_id. Agora, gostaria de mostrar informações do produto (sku, nome) na minha grade de back-end , mas não tenho certeza qual é a melhor prática para fazer isso?

Meu melhor palpite SKUé o seguinte:

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

(código do _prepareCollection() método na minha classe de bloco de grade)

Mas e o nome do produto? Pode ser encontrado em catalog_product_entity_varchar. Meu entendimento é que você pode obtê-lo com bastante facilidade se o seu próprio modelo e coleção de recursos for baseado, Mage_Eav_Model_Entity_Collection_Abstractporque então você poderá usar métodos como joinAttribute. Mas meu modelo é baseado em uma tabela simples e se estende a partir de Mage_Core_Model_Resource_Db_Collection_Abstracte não há joinAttributemétodo disponível.

Então, qual é a melhor maneira de obter o nome do produto nesse caso?

Obrigado pelo seu tempo e ajuda :-)

Atualização: para ser mais preciso, eu estava falando sobre meu modelo e coleção de recursos. Combina uma tabela plana simples com apenas alguns atributos, como

entity_id    product_id    created_at    user_id

Minha intenção é fazer uma grade no back-end, onde mostro algumas estatísticas:

ProductSku    Count(ProductSku)    MAX(created_at)

Até onde eu sei, a melhor maneira de fazer isso é através da classe de blocos de grade e o método a seguir é _prepareCollection().

Meu método fica assim:

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

Isso funciona bem para o sku (que eu me refiro como product_sku no _prepareColums()método. Mas o joinque preciso inserir aqui para obter o nome (e, por exemplo, o fabricante)?

Estou fazendo algo errado porque não posso usar joinLeft()?

Celldweller
fonte

Respostas:

13

Na sua coleção class ( /Some/Module/Model/Mysql4 (or Resource)/YourModel/Collection.php) adicione este método:

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

No seu bloco de grade, use esta função:

 protected function _prepareCollection()
    {
        $collection = Mage::getModel('some/yourmodel')
            ->getCollection()->addProductData();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
mageUz
fonte
Parece promissor, mas infelizmente não funciona. Minha coleção se estende Mage_Core_Model_Resource_Db_Collection_Abstracte recebo um erro Call to undefined method Mycompany_Module_Model_Resource_Mymodel_Collection::joinLeft(). Eu acho que isso é porque eu não uso um modelo de recursos EAV?
precisa
Use juntar-se, em vez de joinLeft, eu editei a resposta
mageUz
doh! Eu me sinto estúpido agora! ;-) E não sei por que tentei. Muito obrigado!
precisa
@ Celldweller, você nem precisa usar absolutamente $this->_map['fields']['sku'] = 'sku'ou semelhante nesse caso. Você precisará apenas se tiver vários nomes de campos idênticos e precisar fazer uma tradução para evitar conflitos. Apenas adicionando sobrecarga para este caso de uso. Em um caso de uso diferente, você pode usar $this->_map['fields']['my_sku'] = 'sku', então você pode usar com a recolha e _prepareColumns: $this->addColumn('my_sku', array(…)), ele vai ajudar você a evitar os conflitos para a filtragem ou colunas de classificação se você tiver um campo skuusado em diferentes tabelas de banco de dados comum para a coleção
Sylvain Rayé
Ótima resposta! Isso pode levar a problemas de "item com o mesmo ID já existe". Um simples $this->getSelect()->group('main_table.product_id');resolve o problema, mas talvez o código possa ser otimizado, para que duplicatas não sejam criadas em primeiro lugar?
Simon
4

Oi Celldweller Espero que você esteja bem :-)

Talvez você tenha se enganado em sua explicação sobre a classe Mage_Core_Model_Resource_Db_Collection_Abstract, você estende uma coleção de recursos e não o modelo porque não deve estender um modelo com uma classe de coleção se quiser respeitar a estrutura do Magento. Estou certo?

Com base na minha correção, vejo diferentes tipos de abordagem, dependendo da frequência com que você deseja obter o atributo de nome do produto. De qualquer forma, acho que fazer uma consulta sql através do Magento Framework é a melhor maneira e eficiente. É mais rápido do que fazer um Mage::getModel('catalog/product')->load(1234)->getName()para cada item carregado. Portanto, de fato, será muito semelhante ao código usado para osku

CÓDIGO NÃO TESTADO

Você deseja essas informações sempre que uma coleção é carregada

Em sua classe de coleção, você pode definir um _beforeLoadmétodo como esse código:

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

Você deseja essas informações apenas para a grade

No seu _prepareCollection, você precisará adicionar um método em sua coleção com o mesmo código que foi feito acima para _beforeLoadpoder preparar a coleção usando esse método. Não use os dois, quero dizer, não use o mesmo código _beforeLoade addProductNamemétodos juntos , use apenas um deles. Aqui está uma amostra:

No seu grid.php:

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

Em seu 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;
}
Sylvain Rayé
fonte
É ótimo ler você de novo :-) Muito obrigado, mas não posso usar o método joinLeft (). Eu acho que é um método somente de modelo de recurso EAV? Você estava certo, eu era impreciso na minha pergunta inicial. Vou editá-lo :)
Celldweller
Ah, e btw: é claro que não quero fazer um produto-> load () em tal situação. Atire em mim se eu ousar! ;-) Meu objetivo é ter tudo em uma coleção para poder classificar e filtrar na grade. Veja a atualização no meu post inicial para obter mais detalhes.
precisa
Então substituir joinLeft por participar, mas você já aceitou a outra resposta, embora eu respondi corretamente e foi o primeiro a responder :-(
Sylvain Rayé
Sim você está certo. Fiquei tão feliz que finalmente funcionou, que não pensei nisso. Sou muito grato a vocês dois. Espero que você possa me perdoar! Eu lhe devo uma cerveja.
Celldweller
ahah não se preocupe. Encontramo-lo em Berlim com prazer :-)
Sylvain Rayé
4

Eu tive quase o mesmo problema, mas não posso adicionar um comentário, porque não tenho 50 reputação. Passei muito tempo tentando descobrir o que estava errado (usei o código de Sylvain Rayé). Por algum motivo, minha coleção de produtos foi filtrada. Então eu encontrei um motivo.

Se você estiver usando algumas ferramentas de importação (magmi, etc), elas geralmente não criam atributos vazios de uma só vez. Portanto, o uso de ->where("product_attribute.attribute_id = ?", $productName->getId())produtos que não possuem esse atributo desaparecerá da seleção.

O caminho certo é usar joinLeftassim:

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

Espero que isso ajude alguém.

spiil
fonte
-2

Exibir atributo personalizado na grade do produto.

Sobrescreva esse bloco Mage_Adminhtml_Block_Catalog_Product_Grid em suas funções de extensão e cópia _prepareCollection e _prepareColumns no arquivo de bloco da sua extensão.

Adicione o código abaixo para selecionar o atributo na função _prepareCollection da grade do produto Mage_Adminhtml_Block_Catalog_Product_Grid antes da linha $ this-> setCollection ($ collection).

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

E, em seguida, abaixo do código da coluna na função _prepareColumns da grade.

$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
fonte
Alterar arquivos principais não é uma boa prática. Mesmo paranew SomeModel()
sv3n
nós não temos que mudou no arquivo core Nós podemos substituir esse bloco (Mage_Adminhtml_Block_Catalog_Product_Grid) para realizar a nossa funcionalidade
Savoo