La mejor práctica para unir atributos de producto

11

Tengo una tabla personalizada con una referencia de producto product_id. Ahora me gustaría mostrar información del producto (sku, nombre) en mi cuadrícula de back-end , pero no estoy seguro de cuál es la mejor práctica para hacer esto.

Mi mejor conjetura SKUes la siguiente:

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

(código del _prepareCollection() método en mi clase de bloque de cuadrícula)

¿Pero qué hay del nombre del producto? Se puede encontrar en catalog_product_entity_varchar. Tengo entendido que puede obtenerlo fácilmente si su propio modelo y colección de recursos se basa en Mage_Eav_Model_Entity_Collection_Abstractporque puede usar métodos como joinAttribute. Pero mi modelo se basa en una tabla simple y se extiende desde Mage_Core_Model_Resource_Db_Collection_Abstracty no hay ningún joinAttributemétodo disponible.

Entonces, ¿cuál es la mejor manera de obtener el nombre del producto en este caso?

Gracias por tu tiempo y ayuda :-)

Actualización: para ser más precisos, estaba hablando de mi modelo de recursos y mi colección. Coincide con una tabla plana simple con solo algunos atributos como

entity_id    product_id    created_at    user_id

Mi intención es cuadricular en el backend donde muestro algunas estadísticas:

ProductSku    Count(ProductSku)    MAX(created_at)

Hasta donde sé, la mejor forma de hacerlo es a través de la clase de bloque de cuadrícula y el método a seguir es _prepareCollection().

Mi método se ve así:

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

Esto funciona bien para el sku (al que me refiero como product_sku en el _prepareColums()método. Pero, ¿ joinqué necesito insertar aquí para obtener el nombre (y, por ejemplo, el fabricante)?

¿Estoy haciendo algo mal porque no puedo usar joinLeft()?

Celldweller
fuente

Respuestas:

13

En su clase de colección ( /Some/Module/Model/Mysql4 (or Resource)/YourModel/Collection.php) agregue 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;
    }

En su bloque de cuadrícula, use esta función:

 protected function _prepareCollection()
    {
        $collection = Mage::getModel('some/yourmodel')
            ->getCollection()->addProductData();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
mageUz
fuente
Esto parece prometedor, pero desafortunadamente no funciona. Mi colección se extiende desde Mage_Core_Model_Resource_Db_Collection_Abstracty recibo un error Call to undefined method Mycompany_Module_Model_Resource_Mymodel_Collection::joinLeft(). ¿Supongo que esto se debe a que no uso un modelo de recursos EAV?
Celldweller
Use join en lugar de joinLeft, edité la respuesta
mageUz
doh! Me siento estúpido ahora! ;-) Y no sé por qué lo intenté. ¡Muchas gracias!
Celldweller
@Celldweller, ni siquiera es absolutamente necesario usar $this->_map['fields']['sku'] = 'sku'o similar en este caso. Solo lo necesita si tiene varios nombres de campos idénticos y necesita hacer una traducción para evitar conflictos. Simplemente agrega gastos generales para este caso de uso. En un caso de uso diferente, puede usarlo $this->_map['fields']['my_sku'] = 'sku', puede usarlo con la colección y _prepareColumns: $this->addColumn('my_sku', array(…))le ayudará a evitar conflictos para el filtrado o la clasificación de columnas si tiene un campo skuusado en diferentes tablas de base de datos comunes a la colección
Sylvain Rayé
¡Gran respuesta! Sin embargo, puede provocar problemas de "elemento con la misma ID". Un simple $this->getSelect()->group('main_table.product_id');resuelve el problema, pero tal vez el código se puede optimizar, de modo que los duplicados no se crean en primer lugar.
Simon
4

Hola Celldweller, espero que estés bien :-)

Tal vez se equivocó en su explicación sobre la clase Mage_Core_Model_Resource_Db_Collection_Abstract, extendió una colección de recursos, no el modelo, porque no debe extender un modelo con una clase de colección si desea respetar la estructura de Magento. Estoy en lo cierto?

Según mi corrección, veo un tipo diferente de enfoque, según la frecuencia con la que desee obtener el atributo de nombre del producto. En cualquier caso, creo que hacer una consulta sql a través del Magento Framework es la mejor manera y eficiente. Es más rápido que hacer un Mage::getModel('catalog/product')->load(1234)->getName()para cada artículo cargado. De hecho, será muy similar al código que usa parasku

CÓDIGO NO PROBADO

Desea esa información cada vez que se carga una colección

Puede en su clase de colección establecer un _beforeLoadmétodo como un 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();
}

Desea esa información SOLO para la cuadrícula

En su _prepareCollection, tendrá que agregar un método en su colección con el mismo código que se hizo anteriormente para _beforeLoadpoder preparar la colección utilizando este método. No use ambos, quiero decir, no use juntos el mismo código _beforeLoady addProductNamemétodos, use solo uno de ellos. Aquí hay una muestra:

En tu grid.php:

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

Into your 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é
fuente
Es bueno volver a leerlo :-) Muchas gracias, pero no puedo usar el método joinLeft (). ¿Supongo que es un método de modelo de recurso EAV solo? Tenías razón, no fui precisa en mi pregunta inicial. Lo editaré :)
Celldweller
Ah y por cierto: Por supuesto, no quiero hacer un producto-> load () en tal situación. ¡Dispárame si alguna vez me atreviera! ;-) Mi objetivo es tenerlo todo en una colección para poder ordenar y filtrar en la cuadrícula. Vea la actualización en mi publicación inicial para más detalles.
Celldweller
Así que reemplace joinLeft por join pero ya aceptó la otra respuesta, aunque respondí correctamente y fui la primera en responder :-(
Sylvain Rayé
Sí, tiene usted razón. Estaba tan feliz de que finalmente funcionó, que no lo pensé. Estoy muy agradecido con los dos. ¡Espero que puedas perdonarme! Te debo una cerveza.
Celldweller el
ajá no te preocupes. Nos vemos en Berlín con mucho gusto :-)
Sylvain Rayé
4

Tuve casi el mismo problema, pero no puedo agregar un comentario, porque no tengo 50 reputación. Pasé mucho tiempo tratando de descubrir qué está mal (usé el código de Sylvain Rayé). Mi colección de productos por alguna razón filtrada. Entonces encontré una razón.

Si utiliza algunas herramientas de importación (magmi, etc.), a menudo no crean atributos vacíos a la vez. Por lo tanto, el uso de ->where("product_attribute.attribute_id = ?", $productName->getId())productos que no tengan este atributo desaparecerá de la selección.

La forma correcta es usar joinLeftasí:

$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 esto ayude a alguien.

spiil
fuente
-2

Mostrar atributo personalizado en la cuadrícula del producto.

Sobrescriba este bloque Mage_Adminhtml_Block_Catalog_Product_Grid en su extensión y copie las funciones _prepareCollection y _prepareColumns en el archivo de bloque de su extensión.

Agregue el código a continuación para seleccionar el atributo en la función _prepareCollection de la cuadrícula del producto Mage_Adminhtml_Block_Catalog_Product_Grid antes de la línea $ this-> setCollection ($ collection).

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

Y luego debajo del código para la columna en la función _prepareColumns de la cuadrícula.

$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
fuente
Cambiar archivos principales no es una buena práctica. Lo mismo paranew SomeModel()
sv3n
no tenemos que cambiar el archivo principal. Podemos sobrescribir ese bloque (Mage_Adminhtml_Block_Catalog_Product_Grid) para lograr nuestra funcionalidad
Savoo