Add an attribute to Magento’s Manage Products admin grid

I have been using Magento for about two and a half years now. I have lost count of the number of times I have thought to myself “I wish I could view/filter/sort by [some product attribute] in the “Manage Products” view of the admin panel, that is not there by default”. Here is a tutorial on how you can add your own custom attribute to this page for this purpose. I will cover date, dropdown and text type attributes.

The columns we’ll be adding to our admin panel

There are modules around that you can install to do this for you, but this tutorial is for people (like me) who want to keep third-party modules installed on their sites to a minimum, and simply want to learn how to do this themselves.

Creating our custom attributes

I’ve created some example custom attributes of different types in order to show you how to add attributes of different input types to the grid. You can add these attributes in Catalog / Manage Attributes / Add New Attribute. The attribute codes I’ve used, which I’ll refer to in the steps below, are:

  • custom_text (Text field input type)
  • custom_date (Date input type)
  • custom_dropdown (Dropdown input type)

Pretty self-explanatory I hope!

Creating our module

If you don’t know the basics of creating a Magento module, here are two other tutorials that explain the process and file structure more fully:

  • http://www.smashingmagazine.com/2012/03/01/basics-creating-magento-module/
  • http://inchoo.net/magento/programming-magento/magento-hello-world-module-extension/

We are going to create the module Mynamespace_Productgrid. Set up the following file structure in your Magento installation, inside the app folder. All you are creating are three files, but the folder structure is important.

app/
  code/
    local/
      Mynamespace/
        Productgrid/
          Block/
            Adminhtml/
              Catalog/
                Product/
                  Grid.php
          etc/
            config.xml
  etc/
    modules/
      Mynamespace_Productgrid.xml

Mynamespace_Productgrid.xml

This file just registers our small module with Magento. If you want to disable it for any reason, just change true to false.

<?xml version="1.0"?>
<config>
	<modules>
		<Mynamespace_Productgrid>
			<active>true</active>
			<codePool>local</codePool>
		</Mynamespace_Productgrid>
	</modules>
</config>

config.xml

You need a config.xml file for any module in Magento. In ours we are going to define a new block and rewrite the default product grid with it.

<config>
	<modules>
		<Mynamespace_Productgrid>
			<version>1.0.0</version>
		</Mynamespace_Productgrid>
	</modules>
	<global>
		<blocks>
			<!--define new block-->
			<productgrid>
				<class>Mynamespace_Productgrid_Block</class>
			</productgrid>
			<!--rewrite Mage_Adminhtml_Block_Catalog_Product_Grid class-->
			<adminhtml>
				<rewrite>
					<catalog_product_grid>Mynamespace_Productgrid_Block_Adminhtml_Catalog_Product_Grid</catalog_product_grid>
				</rewrite>
			</adminhtml>
		</blocks>
	</global>
</config>

Setting up Grid.php

In this file we create the class Mynamespace_Productgrid_Block_Adminhtml_Catalog_Product_Grid (that we already defined in config.xml) which will rewrite the functions of Mage_Adminhtml_Block_Catalog_Product_Grid. This relates to the file app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php You can look at the core files, but edit them at your peril – it’s really bad practice!

<?php
class Mynamespace_Productgrid_Block_Adminhtml_Catalog_Product_Grid extends Mage_Adminhtml_Block_Catalog_Product_Grid
{

    public function setCollection($collection)
    {
        /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */

        $store = $this->_getStore();

        // we'll add more here in the next step

        parent::setCollection($collection);
    }

    protected function _prepareColumns()
    {
        $store = $this->_getStore();

        // we'll add more here in the next step 

        return parent::_prepareColumns();
    }

    protected function getAttributeOptions($_attributeCode) {

		$options = array();

		$collection = Mage::getModel('eav/entity_attribute_option')->getCollection()
			->setStoreFilter()
			->join('attribute','attribute.attribute_id=main_table.attribute_id', 'attribute_code');

        foreach ($collection as $option) {
        	if ($option->getAttributeCode() == $_attributeCode) {
            	$options[$option->getOptionId()] = $option->getValue();
				}
        }

		return $options;

	}


}

Please note that including the getAttributeOptions() function is only necessary if you have a dropdown attribute that you want to add a column for; if not, then leave it out.

Adding column(s) for your custom attribute(s)

For each custom attribute that you want to add, you need to add the following snippets of code in both the setCollection() and _prepareColumns() functions.

Text attributes

I’ll start by showing you how to add the column for the custom_text attribute to your grid:

    public function setCollection($collection)
    {
        /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */

        $store = $this->_getStore();

        if ($store->getId() && !isset($this->_joinAttributes['custom_text'])) {
            $collection->joinAttribute(
                'custom_text',
                'catalog_product/custom_text',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_text');
        }

        parent::setCollection($collection);
    }    

    protected function _prepareColumns() {

        $store = $this->_getStore();

         $this->addColumnAfter('custom_text',
            array(
                  'header'=> Mage::helper('catalog')->__('Text'),
                  'width' => '50px',
                  'type'  => 'text',
                  'index' => 'custom_text'
          	),
 	   'name');

        return parent::_prepareColumns();
    }

The addColumnAfter() function takes 3 parameters – 1. the attribute/column code you want to add, 2. the arguments for the column, 3. the attribute/column code you want to place this column after. In parameter 2 note that we’ve defined the label (‘Text’) and width for our column

So this code should add a column in Manage Products for our custom text attribute, adding it after the Name column.

And, hey presto, it does.

Date attributes

The code you would need to add in order to add a date attribute column is pretty much the same as the text attribute – just change all instances of “custom_text” to “custom_date” (or whatever your attribute code is, obviously!), change the label in line 30, and change the type from “text” to “date” in line 32. All easy stuff.

    public function setCollection($collection)
    {
        /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */

        $store = $this->_getStore();

        if ($store->getId() && !isset($this->_joinAttributes['custom_date'])) {
            $collection->joinAttribute(
                'custom_date',
                'catalog_product/custom_date',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_date');
        }

        parent::setCollection($collection);
    }    

    protected function _prepareColumns() {

        $store = $this->_getStore();

         $this->addColumnAfter('custom_date',
            array(
                  'header'=> Mage::helper('catalog')->__('Date'),
                  'width' => '50px',
                  'type'  => 'date',
                  'index' => 'custom_date'
          	),
 	   'name');

        return parent::_prepareColumns();
    }

I’ve also specified this column to be after my custom_text column.

Dropdown attributes

Same idea here, except we specify the type as ‘options’, and we add a new line at line 35 to define the options for the dropdown, using our getAttributeOptions() function.

    public function setCollection($collection)
    {
        /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */

        $store = $this->_getStore();

        if ($store->getId() && !isset($this->_joinAttributes['custom_dropdown'])) {
            $collection->joinAttribute(
                'custom_dropdown',
                'catalog_product/custom_dropdown',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_dropdown');
        }

        parent::setCollection($collection);
    }    

    protected function _prepareColumns() {

        $store = $this->_getStore();

         $this->addColumnAfter('custom_dropdown',
            array(
                  'header'=> Mage::helper('catalog')->__('Dropdown'),
                  'width' => '50px',
                  'type'  => 'options',
                  'index' => 'custom_dropdown',
                  'options' => $this->getAttributeOptions('custom_dropdown')
          	),
 	   'custom_date');

        return parent::_prepareColumns();
    }

The whole thing

So to put all of that together, we end up with the following file for Grid.php:

<?php
class Myname_Productgrid_Block_Adminhtml_Catalog_Product_Grid extends Mage_Adminhtml_Block_Catalog_Product_Grid
{

    /* Overwritten to be able to add custom columns to the product grid. Normally
     * one would overwrite the function _prepareCollection, but it won't work because
     * you have to call parent::_prepareCollection() first to get the collection.
     *
     * But since parent::_prepareCollection() also finishes the collection, the
     * joins and attributes to select added in the overwritten _prepareCollection()
     * are 'forgotten'.
     *
     * By overwriting setCollection (which is called in parent::_prepareCollection()),
     * we are able to add the join and/or attribute select in a proper way.
     *
     */
    public function setCollection($collection)
    {
        /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */

        $store = $this->_getStore();

        if ($store->getId() && !isset($this->_joinAttributes['custom_date'])) {
            $collection->joinAttribute(
                'custom_date',
                'catalog_product/custom_date',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_date');
        }

        if ($store->getId() && !isset($this->_joinAttributes['custom_text'])) {
            $collection->joinAttribute(
                'custom_text',
                'catalog_product/custom_text',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_text');
        }

        if ($store->getId() && !isset($this->_joinAttributes['custom_dropdown'])) {
            $collection->joinAttribute(
                'custom_dropdown',
                'catalog_product/custom_dropdown',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('custom_dropdown');
        }

        parent::setCollection($collection);
    }

    protected function _prepareColumns()
    {
        $store = $this->_getStore();

        $this->addColumnAfter('custom_date',
           array(
                 'header'=> Mage::helper('catalog')->__('Date'),
                 'width' => '50px',
                 'type'  => 'datetime',
                 'index' => 'custom_date',
           ),
		 'name');
         $this->addColumnAfter('custom_text',
            array(
                  'header'=> Mage::helper('catalog')->__('Text'),
                  'width' => '50px',
                  'type'  => 'text',
                  'index' => 'custom_text',
          	),
 		 'custom_date');
         $this->addColumnAfter('custom_dropdown',
           	array(
                  'header'=> Mage::helper('catalog')->__('Dropdown'),
                  'width' => '100px',
                  'type'  => 'options',
                  'index' => 'custom_dropdown',
				  'options' => $this->getAttributeOptions('custom_dropdown')
          	),
 		 'custom_date');

        return parent::_prepareColumns();
    }

	protected function getAttributeOptions($_attributeCode) {

		$options = array();

		$collection = Mage::getModel('eav/entity_attribute_option')->getCollection()
			->setStoreFilter()
			->join('attribute','attribute.attribute_id=main_table.attribute_id', 'attribute_code');

        foreach ($collection as $option) {
        	if ($option->getAttributeCode() == $_attributeCode) {
            	$options[$option->getOptionId()] = $option->getValue();
				}
        }

		return $options;

	}

}

Troubleshooting

If you have followed the steps correctly, the only reason I know of that these steps wouldn’t work for your site, is if you have another module that is already rewriting the Mage_Adminhtml_Block_Catalog_Product_Grid block. You can find this out by searching the files in app/code/local and app/code/community for the phrase extends Mage_Adminhtml_Block_Catalog_Product_Grid. I won’t go into how you can overcome this here, but here’s a tutorial that may help: Resolving Magento extension conflicts.

Summary

We have created custom date, text and dropdown attributes and learned how to use a simple module to add these attributes to the “Manage Products” grid in the admin panel.

Credit

I’d like to give most of the credit for this tutorial to the contributors of the following pages. Basically I’ve put their ideas together in one space as I was surprised not to find the tutorial I needed already out there.

2 thoughts on “Add an attribute to Magento’s Manage Products admin grid

  1. You should put the completed code on github so people can easily use it and update it if they find some issues.

    Thanks for the tutorial!

Leave a Reply

Your email address will not be published. Required fields are marked *