Ajouter des colonnes à la grille produits sans surcharge

  • De le 13 octobre 2014
  • Difficulté : 2/4

Ajouter des colonnes à la grille produits sans surcharge Ajouter de nouvelles colonnes à la grille des produits du backoffice sans surcharger le block dédié à la génération de cette grille n'est pas si évident. Cet article propose une manière élégante d'afficher de nouveaux attributs. Nous ajoutons les attributs "image" et "special_price".

L'objectif est d'ajouter de nouveaux attributs à la grille des produits, sans surcharger la classe Mage_Adminhtml_Block_Catalog_Product_Grid.

Nous souhaitons ajouter l'image et le prix promo du produit. Pour y arriver nous utiliserons uniquement les observers.

Product Grid

Architecture du module

Développement du module

La principale difficulté reste l'utilisation des événements appropriés, et il en maque ici cruellement... La classe Mage_Adminhtml_Block_Widget_Grid dont héritent les blocs de type grid en est totalement dépourvue.

Mais heureusement, nous avons à notre disposition l'événement core_block_abstract_to_html_before, déclaré juste avant le rendu HTML des blocs. Il nous permet d’interagir sur le bloc avant son rendu, dans notre cas ajouter de nouvelles colonnes.

Le seul petit problème est qu'à ce stade aucune collection n'est instanciée. Il nous faut pourtant spécifier les nouveaux attributs à sélectionner.

Nous n'avons pas d'autres choix que d'utiliser l'événement catalog_product_collection_load_before pour charger les nouveaux attributs.

Une petite sécurité sera préalablement utilisée pour éviter que ces attributs ne soient chargés ailleurs que sur la page contenant la grille des produits. Nous utiliserons le registre sur les événements suivants :

  • controller_action_predispatch_adminhtml_catalog_product_index
  • controller_action_predispatch_adminhtml_catalog_product_grid

Ces événements interviennent avant que l'action du contrôleur ne soit exécutée. Le deuxième événement est appelé lors du rafraîchissement de la grille en Ajax.

Ainsi nous pouvons déclarer le fichier de configuration du module :

app/code/local/Magentix/ProductColumns/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Magentix_ProductColumns>
            <version>0.1.0</version>
        </Magentix_ProductColumns>
    </modules>
    <global>
        <blocks>
            <product_columns>
                <class>Magentix_ProductColumns_Block</class>
            </product_columns>
        </blocks>
        <models>
            <product_columns>
                <class>Magentix_ProductColumns_Model</class>
            </product_columns>
        </models>
    </global>
    <adminhtml>
        <events>
            <!-- Use register on predispatch -->
            <controller_action_predispatch_adminhtml_catalog_product_grid>
                <observers>
                    <product_columns_register_adminhtml_catalog_product_grid>
                        <class>product_columns/observer</class>
                        <method>registerCatalogProductGrid</method>
                    </product_columns_register_adminhtml_catalog_product_grid>
                </observers>
            </controller_action_predispatch_adminhtml_catalog_product_grid>
            <controller_action_predispatch_adminhtml_catalog_product_index>
                <observers>
                    <product_columns_register_adminhtml_catalog_product_index>
                        <class>product_columns/observer</class>
                        <method>registerCatalogProductGrid</method>
                    </product_columns_register_adminhtml_catalog_product_index>
                </observers>
            </controller_action_predispatch_adminhtml_catalog_product_index>
            <!-- Add columns to product grid -->
            <core_block_abstract_to_html_before>
                <observers>
                    <product_columns_add_columns_to_product_grid>
                        <class>product_columns/observer</class>
                        <method>addColumnsToProductGrid</method>
                    </product_columns_add_columns_to_product_grid>
                </observers>
            </core_block_abstract_to_html_before>
            <!-- Add attributes to collection -->
            <catalog_product_collection_load_before>
                <observers>
                    <product_columns_add_attributes_to_collection>
                        <class>product_columns/observer</class>
                        <method>addAttributesToProductCollection</method>
                    </product_columns_add_attributes_to_collection>
                </observers>
            </catalog_product_collection_load_before>
        </events>
    </adminhtml>
</config>

La suite se passe dans la classe Magentix_ProductColumns_Model_Observer :

app/code/local/Magentix/ProductColumns/Model/Observer.php

<?php

class Magentix_ProductColumns_Model_Observer
{

    protected function _getColumns()
    {
        /* Afficher */
    }

    public function registerCatalogProductGrid(Varien_Event_Observer $observer)
    {
        /* Afficher */
    }

    public function addAttributesToProductCollection(Varien_Event_Observer $observer)
    {
        /* Afficher */
    }

    public function addColumnsToProductGrid(Varien_Event_Observer $observer)
    {
        /* Afficher */
    }

}

Détaillons les 4 méthodes qu'elle contient.

_getColumns

Cette méthode nous permet de déclarer les colonnes que nous souhaitons ajouter. Elle retourne un tableau contenant les données classiques d'une colonne :

app/code/local/Magentix/ProductColumns/Model/Observer.php

    /**
     * Retrieve new columns information
     *
     * @return array
     */
    protected function _getColumns()
    {
        $storeId = (int) Mage::app()->getRequest()->getParam('store', 0);
        $store = Mage::app()->getStore($storeId);

        return array(
            'special_price' => array( // Attribute code
                'data' => array( // Column data
                    'header'        => Mage::helper('catalog')->__('Special Price'),
                    'index'         => 'special_price',
                    'type'          => 'price',
                    'currency_code' => $store->getBaseCurrency()->getCode(),
                ),
                'after' => 'price', // After column code (string or null)
            ),
            'image' => array( // Attribute code
                'data' => array( // Column data
                    'header'   => Mage::helper('catalog')->__('Image'),
                    'index'    => 'image',
                    'type'     => 'image', // New type (added in addColumnsToProductGrid method)
                    'width'    => '100px',
                ),
                'after' => 'entity_id', // After column code (string or null)
            ),
        );
    }

Notez que le type image n'existe pas de base dans Magento. C'est un nouveau type que nous allons ajouter depuis la méthode addColumnsToProductGrid.

registerCatalogProductGrid

Nous utilisons le registre pour spécifier que nous nous trouvons sur la page de la grille des produits. Cela nous permet d'éviter par la suite le chargement des attributs supplémentaires ailleurs que sur cette page.

app/code/local/Magentix/ProductColumns/Model/Observer.php

    /**
     * Register adminhtml catalog product grid
     *
     * @param Varien_Event_Observer $observer
     *
     * @return Magentix_ProductColumns_Model_Observer
     */
    public function registerCatalogProductGrid(Varien_Event_Observer $observer)
    {
        Mage::register('adminhtml_catalog_product_grid', true);

        return $this;
    }

addAttributesToProductCollection

Nous ajoutons les attributs issues de la méthode _getColumns :

app/code/local/Magentix/ProductColumns/Model/Observer.php

    /**
     * Add attributes to products collection
     *
     * @param Varien_Event_Observer $observer
     *
     * @return Magentix_ProductColumns_Model_Observer
     */
    public function addAttributesToProductCollection(Varien_Event_Observer $observer)
    {
        if(Mage::registry('adminhtml_catalog_product_grid')) {

            $storeId = (int) Mage::app()->getRequest()->getParam('store', 0);

            /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */
            $collection = $observer->getEvent()->getCollection();

            $columns = $this->_getColumns();

            if ($storeId) {
                foreach($columns as $attribute => $column) {
                    $collection
                        ->joinAttribute(
                            $attribute,
                            'catalog_product/' . $attribute,
                            'entity_id',
                            null,
                            'left',
                            $storeId
                        );
                }
            } else {
                foreach($columns as $attribute => $column) {
                    $collection->addAttributeToSelect($attribute);
                }
            }

        }

        return $this;
    }

addColumnsToProductGrid

Nous ajoutons les nouvelles colonnes définies par la méthode _getColumns. Nous ajoutons également dans cette méthode le nouveau type de filtre et de rendu pour image. Vous pouvez ajouter autant de type que vous le souhaitez, c'est un moyen efficace pour faire de jolies grilles.

app/code/local/Magentix/ProductColumns/Model/Observer.php

    /**
     * Add columns to product grid
     *
     * @param Varien_Event_Observer $observer
     *
     * @return Magentix_ProductColumns_Model_Observer
     */
    public function addColumnsToProductGrid(Varien_Event_Observer $observer)
    {
        /* @var $block Mage_Core_Block_Abstract */
        $block = $observer->getEvent()->getBlock();

        if ($block instanceof Mage_Adminhtml_Block_Catalog_Product_Grid) {
            /* @var $block Mage_Adminhtml_Block_Catalog_Product_Grid */

            $block->setColumnFilters(
                array('image' => 'product_columns/filter_image') // Magentix_ProductColumns_Block_Filter_Image
            );

            $block->setColumnRenderers(
                array('image' => 'product_columns/renderer_image') // Magentix_ProductColumns_Block_Renderer_Image
            );

            $columns = $this->_getColumns();

            foreach($columns as $attribute => $column) {

                /* Add Column */
                if($column['after']) {
                    $block->addColumnAfter($attribute, $column['data'], $column['after']);
                } else {
                    $block->addColumn($attribute, $column['data']);
                }

            }

        }

        return $this;
    }

La suite est utile pour afficher l'image du produit. Nous avons besoin pour la colonne image de définir le rendu et le fitre à appliquer sur la collection.

Tout d'abord la colonne doit afficher une balise img contenant le lien vers l'image.

Petite mise à jour suite à un commentaire judicieux, l'image est maintenant redimensionnée.

app/code/local/Magentix/ProductColumns/Block/Renderer/Image.php

<?php

class Magentix_ProductColumns_Block_Renderer_Image
    extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{

    /**
     * Renders grid column
     *
     * @param Varien_Object $row
     *
     * @return string
     */
    public function _getValue(Varien_Object $row)
    {
        $data = parent::_getValue($row);

        $imageHtml = '';

        if($data && $data !== 'no_selection') {
            // $url = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'catalog/product' . $data;
            // $imageHtml = '<img src="' . $url . '" alt="" width="100" />';

            // UPDATE 14/10/2014
            $imageHtml = '<img src="' . $this->_resize($data, 100) . '" alt="" width="100" />';
        }

        return $imageHtml;
    }

    /**
     * Resize Image
     *
     * @param string $image
     * @param int $size
     *
     * @return string
     */
    protected function _resize($image, $size)
    {
        $media = Mage::getBaseDir(Mage_Core_Model_Store::URL_TYPE_MEDIA) . DS;

        $path    = $media . 'catalog' . DS . 'product' . preg_replace('/\//', DS, $image);
        $newPath = $media . 'catalog' . DS . 'product' . DS . 'grid' . preg_replace('/\//', DS, $image);

        if(!file_exists($newPath) && file_exists($path)) {

            $imageObj = new Varien_Image($path);
            $imageObj->constrainOnly(true);
            $imageObj->keepAspectRatio(true);
            $imageObj->keepFrame(false);
            $imageObj->resize($size);
            $imageObj->save($newPath);

        }

        return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'catalog/product/grid' . $image;
    }

}

Le filtre lui est de type checkbox. Le bloc hérite donc de la classe correspondante à l'affichage de ce filtre. Nous ne déclarons que la méthode utilisée pour appliquer le filtre sur la collection :

app/code/local/Magentix/ProductColumns/Block/Filter/Image.php

<?php

class Magentix_ProductColumns_Block_Filter_Image
    extends Mage_Adminhtml_Block_Widget_Grid_Column_Filter_Checkbox
{

    /**
     * Retrieve Condition
     *
     * @return array
     */
    public function getCondition()
    {
        if (is_null($this->getValue())) {
            return $this->getColumn()->getValue();
        }
        else {
            $operator = $this->getValue() ? 'nin' : 'in';
            return array(
                array($operator => array('', 'no_selection')),
            );
        }
    }

}

Pour finir, la déclaration du module :

app/etc/modules/Magentix_ProductColumns.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Magentix_ProductColumns>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Adminhtml />
                <Mage_Catalog />
            </depends>
        </Magentix_ProductColumns>
    </modules>
</config>

Bonus : supprimer une colonne

Il est tout à fait possible de supprimer une colonne par défaut de Magento par l'intermédiaire d'un autre événement : adminhtml_block_html_before

app/code/local/Magentix/ProductColumns/etc/config.xml

<!-- ... -->
    <adminhtml>
        <events>
            <!-- Remove columns -->
            <adminhtml_block_html_before>
                <observers>
                    <product_columns_remove_columns>
                        <class>product_columns/observer</class>
                        <method>removeProductGridColumns</method>
                    </product_columns_remove_columns>
                </observers>
            </adminhtml_block_html_before>
        </events>
    </adminhtml>
<!-- ... -->

La méthode removeProductGridColumns est simpliste :

app/code/local/Magentix/ProductColumns/Model/Observer.php

    /**
     * Remove column from product grid
     *
     * @param Varien_Event_Observer $observer
     *
     * @return Magentix_ProductColumns_Model_Observer
     */
    public function removeProductGridColumns(Varien_Event_Observer $observer)
    {
        /* @var $block Mage_Core_Block_Abstract */
        $block = $observer->getEvent()->getBlock();

        if ($block instanceof Mage_Adminhtml_Block_Catalog_Product_Grid) {
            /* @var $block Mage_Adminhtml_Block_Catalog_Product_Grid */

            $block->removeColumn('set_name');

        }

        return $this;
    }
commentaires

Commentez cet article : Ajouter des colonnes à la grille produits sans surcharge