Add Custom Tabs to the Magento Product Admin

In Magento eCommerce, it is possible to add new attributes to product models and edit the values for these attributes on the Product edit page. This is fine if we only want to add standard data (attributes) to a product, but what if we wanted to do something a little different?

This article explains how to build a simple custom Magento extension that will add a new tab to the product edit page and provide a facility for processing that data when the user hits the Save button.

You might also be interested in Magento product tabs for your frontend store.

Creating the Magento Extension

The first step is to create the extension's setup file, which loads the extension into Magento.

app/etc/modules/Fishpig_Customtabs.xml

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

This file will register the extension in Magento, telling it to look in the app/code/local/Fishpig/Customtabs/ directory for the extension's code.

app/code/local/Fishpig/Customtabs/etc/config.xml

The next file we make is the extension's config file. This file is a more detailed setup file, containing information about the extension's classes, layout files and everything else we need to make it work.

Notice the events section of this file (below)? To save the product data, we listen for an event that is triggered when ever a product is saved in the Magento Admin. This allows us to access the custom data we have created in the tab and process/save it.

<?xml version="1.0"?>
<config>
    <modules>
        <Fishpig_CustomTabs>
            <version>0.1.0</version>
        </Fishpig_CustomTabs>
    </modules>
    <global>
        <blocks>
            <customtabs>
                <class>Fishpig_Customtabs_Block</class>
            </customtabs>
        </blocks>
        <models>
            <customtabs>
                <class>Fishpig_Customtabs_Model</class>
            </customtabs>
        </models>
    </global>
    <adminhtml>
    	<layout>
    		<updates>
    			<customtabs>
    				<file>customtabs.xml</file>
    			</customtabs>
    		</updates>
    	</layout>
        <events>
            <catalog_product_save_after>
                <observers>
                    <fishpig_save_product_data>
                        <type>singleton</type>
                        <class>customtabs/observer</class>
                        <method>saveProductTabData</method>
                    </fishpig_save_product_data>
                </observers>
            </catalog_product_save_after>
        </events>
    </adminhtml>
</config>

app/code/local/Fishpig/Customtabs/Block/Adminhtml/Catalog/Product/Tab.php

<?php

class Fishpig_Customtabs_Block_Adminhtml_Catalog_Product_Tab 
extends Mage_Adminhtml_Block_Template
implements Mage_Adminhtml_Block_Widget_Tab_Interface {

	/**
	 * Set the template for the block
	 *
	 */
	public function _construct()
	{
		parent::_construct();
		
		$this->setTemplate('customtabs/catalog/product/tab.phtml');
	}
	
	/**
	 * Retrieve the label used for the tab relating to this block
	 *
	 * @return string
	 */
    public function getTabLabel()
    {
    	return $this->__('My Custom Tab');
    }
    
    /**
     * Retrieve the title used by this tab
     *
     * @return string
     */
    public function getTabTitle()
    {
    	return $this->__('Click here to view your custom tab content');
    }
    
	/**
	 * Determines whether to display the tab
	 * Add logic here to decide whether you want the tab to display
	 *
	 * @return bool
	 */
    public function canShowTab()
    {
		return true;
    }
    
    /**
     * Stops the tab being hidden
     *
     * @return bool
     */
    public function isHidden()
    {
    	return false;
    }

	
	/**
	 * AJAX TAB's
	 * If you want to use an AJAX tab, uncomment the following functions
	 * Please note that you will need to setup a controller to recieve
	 * the tab content request
	 *
	 */
	/**
	 * Retrieve the class name of the tab
	 * Return 'ajax' here if you want the tab to be loaded via Ajax
	 *
	 * return string
	 */
#	public function getTabClass()
#	{
#		return 'my-custom-tab';
#	}

	/**
	 * Determine whether to generate content on load or via AJAX
	 * If true, the tab's content won't be loaded until the tab is clicked
	 * You will need to setup a controller to handle the tab request
	 *
	 * @return bool
	 */
#	public function getSkipGenerateContent()
#	{
#		return false;
#	}

	/**
	 * Retrieve the URL used to load the tab content
	 * Return the URL here used to load the content by Ajax 
	 * see self::getSkipGenerateContent & self::getTabClass
	 *
	 * @return string
	 */
#	public function getTabUrl()
#	{
#		return null;
#	}

}

This is a big file and is the main code file for our tab. This file extends Mage_Adminhtml_Block_Template, which is the base Adminhtml template and doesn't do much. Your tab block can extend any block file in Magento, for example, in FishPig's WordPress Integration, we use this method to extend the grid class to display a list of WordPress posts.

On it's own, the above code won't do anything as we haven't included it yet. Let's include it now by creating a layout XML file for our extension.

app/design/adminhtml/default/default/layout/customtabs.xml

This is the layout file for the Adminhtml section of the extension. In here we will include the tab on the Magento Product edit page.

<?xml version="1.0"?>
<layout>
	<adminhtml_catalog_product_edit>
		<reference name="product_tabs">
			<action method="addTab">
				<name>my_custom_tab</name>
				<block>customtabs/adminhtml_catalog_product_tab</block>
			</action>
		</reference>
	</adminhtml_catalog_product_edit>
</layout>

This file is quite simple but without it, nothing will show on the product edit page. The last thing to do before we can see our tab in action is to create our new template file.

app/design/adminhtml/default/default/template/customtabs/catalog/product/tab.phtml

<?php
/**
 * Custom tab template
 */
?>
<div class="input-field">
 <label for="custom_field">Custom Field</label>
 <input type="text" class="input-text" name="custom_field" id="custom_field" />
</div>

Although this example is simple, you could enter anything you want in here. Using this and your block hopefully you can see how truly limitless the options are to you.

Testing Our Custom Magento Admin Tab

Now that we have the code in place, let's refresh our cache, go to a product edit page and see the tab in action!

Did it work? If it didn't work, check through your XML again as a slight error in any of that will stop everything working.

Now that we have our tab, let's take a look at how to process the data once the user hits the 'Save' button.

Saving our Custom Tab Data

To access the data during the saving process, we have hooked into an event called catalog_product_save_after (see config.xml above). This event is triggered each time a product model is saved. As we have declared our observer for this event inside the adminhtml block, we will only trigger our code when a product model is saved from the Magento Admin.

app/code/local/Fishpig/Customtabs/Model/Observer.php

<?php
 
class Fishpig_Customtabs_Model_Observer
{
	/**
	 * Flag to stop observer executing more than once
	 *
	 * @var static bool
	 */
	static protected $_singletonFlag = false;

	/**
	 * This method will run when the product is saved from the Magento Admin
	 * Use this function to update the product model, process the 
	 * data or anything you like
	 *
	 * @param Varien_Event_Observer $observer
	 */
	public function saveProductTabData(Varien_Event_Observer $observer)
	{
		if (!self::$_singletonFlag) {
			self::$_singletonFlag = true;
			
			$product = $observer->getEvent()->getProduct();
		
			try {
				/**
				 * Perform any actions you want here
				 *
				 */
				$customFieldValue =  $this->_getRequest()->getPost('custom_field');

				/**
				 * Uncomment the line below to save the product
				 *
				 */
				//$product->save();
			}
			catch (Exception $e) {
				Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
			}
		}
	}
     
	/**
	 * Retrieve the product model
	 *
	 * @return Mage_Catalog_Model_Product $product
	 */
	public function getProduct()
	{
		return Mage::registry('product');
	}
	
    /**
     * Shortcut to getRequest
     *
     */
    protected function _getRequest()
    {
        return Mage::app()->getRequest();
    }
}

Although this code doesn't do anything with the data, it hopefully illustrates how to access the data, after which you can do what ever you want.

Wrapping it Up

The extension you have just written is a simple extension that will allow you to add custom tabs in the Product edit page of the Magento Admin. This concept can be applied to any of the other sections in Magento (categories, customers etc) so take the time to study the code and understand how it works. For a more practical implementation of this, take a look at the code in FishPig's WordPress Integration as this uses this system to add custom data grid's as AJAX tabs to the Product edit page of the Magento Admin.

63 thoughts on “Add Custom Tabs to the Magento Product Admin”

  • Mike Girouard

    Oh very nice. I've seen other ways of handling this and I think using the event system is by far the most elegant.

    One correction: It looks like a HTML paragraph is getting pulled into the bottom of the config.xml. If someone tries to copy/paste that, it'll probably wind up not working.

    Thanks for the excellent walkthrough.

    Mike G.

    Reply
  • BT

    Thanks Mike, well spotted!

    I have updated the article and removed the rogue paragraph tag.

    Reply
  • Horatiu Taina

    Hi, fishpig!

    I've used your code for removing some tabs, instead of adding!

    Thank you for posting this article!

    I also have one question, if you don't mind!

    This is my code:
    http://www.magentocommerce.com/boards/viewreply/266250/

    Can you please tell me if overwriting injectTabs like that is going to affect my store in any way, except what I wanted to do?

    Thank you in advance!
    HT

    Reply
  • Snowcore

    It is also possible to add tabs using layout files. I think layouts is a better way to add adminhtml tabs, than using observers.

    Reply
    • BT

      You're right, it definitely would be a better approach to add tabs using layout XML. I tried this for several hours before giving up and looking for a new method. The closest I could get using layout XML was to add a new tab set, rather than add a tab into the current set. The main problem with this is that the JS didn't open/close tabs from different sets correctly.

      If you find a way to do this using layout XML it would be good to see

      Reply
  • Jona

    Hi,

    I think XML layouts is the best way too.
    Take this as an exemple (basic i recognize)
    I maked that for a new product type i've created so my layout handle was 'adminhtml_catalog_product_MYTYPE' but you can do that in edit action too.





    tab_namemodule/adminhtml_catalog_product_edit_tab_tabname




    Your Block class will implements Mage_Adminhtml_Block_Widget_Tab_Interface.
    It will help you to know what method to use.

    In my case, it was just a tab 'Mage_Adminhtml_Block_Widget_Grid' but you can do more of course...
    Thanks for share your technic ;)

    NB: Sorry for possible english mistakes, i speak french ;)

    Bye,

    Jona

    Reply
  • Jona

    Shit it's impossible to post XML :s
    Modertor, please take care, my last post is not publishable

    TY

    Bye

    Reply
  • Jona

    Hi,

    I think XML layouts is the best way too.
    Take this as an exemple (basic i recognize)
    I maked that for a new product type i’ve created so my layout handle was ‘adminhtml_catalog_product_MYTYPE’ but you can do that in edit action too.

    <adminhtml_catalog_product_YOURTYPE>
    <reference name="product_tabs">
    <action method="addTab"><name>tabname</name><block>rate/adminhtml_catalog_product_edit_tab_tabname</block></action>
    </reference>
    </adminhtml_catalog_product_YOURTYPE>

    Your Block class will implements Mage_Adminhtml_Block_Widget_Tab_Interface.
    It will help you to know what method to use.

    In my case, it was just a tab ‘Mage_Adminhtml_Block_Widget_Grid’ but you can do more of course…
    Thanks for share your technic ;)

    NB: Sorry for possible english mistakes, i speak french ;)

    Bye,

    Jona

    Reply
    • BT

      @Jona

      I haven't had the chance to test this yet but this looks great! I had tried for a while to use XML to inject tabs, but never thought of using the action/method technique.

      Good job!

      Reply
  • Jona

    @fishpig

    Thank you.
    Be sure of it, it works great ! ;)

    Just think this:

    You do

    <reference name="my_tabs_block">
    <action method="myfunc">
    <param1>value</param1>
    <param2>value</param2>
    </action>
    </reference>

    But where is myfunc?
    ... Simply in the reference block (or his parent in this case Mage_Adminhtml_Block_Widget_Tabs) and the function params are gived by childs nodes of action tag.
    This works for all functions. You can imagine doing a lot of things with XML ;)

    ;)

    Bye, Jona

    Reply
  • top rated antivirus

    I just followed your tutorial
    I am able to add a tab into the admin side
    Now I need to add the input box,I have done that part
    finaly I need to store this value.Which are the tables I need to check for that
    please help me
    Thanks

    Reply
  • magento

    i want add custom options tab for custom modules which are showing in product tab.
    please help me on this topic....

    Reply
  • FX

    Hi,

    i love this article, so useful...

    I'm trying to replace your template call by a php block call, and i encounter hardships...

    here is my content line :
    'content' => $block->getLayout()->createBlock('flash/adminhtml_catalog_product_edit_tabs_flashsales')->toHtml();

    and i obtain "Fatal error: Call to a member function toHtml() on a non-object"

    i checked syntax into Debonix_Flash_Block_Adminhtml_Catalog_Product_Edit_Tabs_Flashsales class several times, and i noticed nothing irrevelant. here is my block class :
    class Debonix_Flash_Block_Adminhtml_Catalog_Product_Edit_Tabs_Flashsales extends Mage_Adminhtml_Block_Widget_Form
    {
    protected function _prepareForm()
    {
    // code
    }
    }

    It is good ? what can prevent this block to fail ?

    thank you

    Reply
  • FX

    sorry to have bothered, it was a router misbehave because of an error in config.xml.

    Magento provides an observer to add a new column in a grid ? (i would like to add category information into cms block information...)

    Reply
  • Milos

    Hi, great tutorial.
    I'm new here, before the 7 days I started to work in Magento.
    I am interested if anyone is still developing the module, I need to add new fields and record the value in this new tab ?
    Any advice is useful to me, I have little time to develop this.
    Thanks

    Reply
  • Anjan

    It really Worked thanks, Fishpig

    Reply
  • Rocco

    Can you use this for the Category editor as well?

    Reply
  • thilo

    Nice tutorial. Just tried this with Magento Version 1.5.1.0 and adapted it to add custom tabs to the magento customer admin. Thx!

    Reply
  • mattclegg

    Im sure it works but the XML is unreadable / unusable because its being incorrectly formatted.

    Reply
  • Narendra

    Hi Fishpig,

    I can not thank you enough for this post. I was searching for this for a long time and I found your article which works so fantastic in Magento 1.5.1.0 and it saves me.

    Thank You Once Again :)

    Reply
  • oschlieb

    Hi, this is a great intro into adding tab content for Product pages. I'm having trouble seeing if the data is actually saved when putting something into the form field. It returns blank every time I come back to the product and tab? I have updated the file names and some of the locations but everything else seems to be working fine.

    Any help is appreciated.

    Reply
  • BT

    The above example doesn't actually save any data, it just provides a framework for you to add your own code to process/save the custom data you add.

    Reply
  • CMani

    Hi thx for the great post. I can't able to get Observer.php working. Seems events is not getting hooked properly. Can you post the working config.xml pl.

    Reply
  • Tom Tep

    It was error in Magento 1.4.2

    Reply
  • Tom Tep

    ohh, it showed. thank you, http://fishpig.co.uk/

    Reply
  • MagePsycho

    Is there a way to set position of tabs created this way?
    Thanks

    Reply
  • Vishal

    Hi fishpig,

    It is such a great article. but i am using grid in these tab, grid came successfully but data did not save. and on the right side of tab it gave a yellow icon and the tittle of these icon is
    " This tab contains invalid data. Please solve the problem before saving. ".
    and in my tab.php, I Wrote the following code

    $this->addTab('devicetype_section', array(
    'label' => Mage::helper('devicetype')->__('Related Brands'),
    'title' => Mage::helper('devicetype')->__('Related Brands'),
    'content' => $this->getLayout()->createBlock('devicetype/adminhtml_devicetype_relatedbrandgrid')->toHtml(),

    ));
    to create the tab

    and in relatedbrandgrid.php just copy the default grid.php

    plz give me the soluation for these how to remove these error and save data..

    " This tab contains invalid data. Please solve the problem before saving."

    Thanx

    Vishal

    Reply
Leave a Reply
Post your comment

FishPig Ltd