Ultimate Magento Admin Interface Customization Guide

Would it be useful if you could create custom columns in your Magento admin panel to have a better overview of grids e.g. with orders or customers?



Would you like to know how it is possible to set up a separate admin menu and add a grid/form to it? And what if you could organize your customers into a separately administered category structure so that you could offer custom deals to each of the customer categories? These needs can all be addressed within the Magento ecommerce framework by adding customized admin menus, grids and forms.

In this article we will describe:

  • How to create a new Magento admin menu
  • How to add a grid to the menu
  • How to create forms and inputs within them
  • How tabs work in the forms

 

There are two approaches to this challenge:

  1. A new admin menu should be set up for category listing and editing, and then adding a new tab to the customer editing where we can assign the relevant categories to the given customer.
  2. We add a customer list to the category editing where we can assign the customers to the given category. In the final solution we describe this second option because here we can handle these settings in one single location.

Planned steps of implementation:

  1. Planning of database items (database modifications are not described in this article)
  2. Creating new modules a (Customercategory)
    • Könyvtár struktúra létrehozása
    • magento customer category directory structure
    • Creating the necessary model, resource model, helper installers
    • App/etc/modules/Aion_Customercategory.xml
    • Creating the config.xml in the module’s etc library
    • Creating controllers
    • Creating Blocks
    • Creating Layout
    • Creating language files (csv)

Creating the module

Within Magento, when creating a new module, first we need to create a library (Customercategory) – where we want to place the module –, in the selected pool (local in our case), under namespace (aion). At the same time we create [module name].xml under app/etc/modules.


<?xml version="1.0"?>
 <config>
     <modules>
         <!-- namespace_modulename -->
         <Aion_Customercategory>
             <active>true</active>
             <!-- pool -->
             <codePool>local</codePool>
         </Aion_Customercategory>
     </modules>
 </config>

Library structure of the module:

  • Block – forms, grids and similar classes to be shown will be placed here
  • controllers – this contains controllers
  • etc – location of xml files to be used (config.xml, adminhtml.xml, …)
  • Helper – location of helper files
  • Model – location of model files
  • sql – location of database installer/update files

We need to set the basic actions in the config.xml of the module’s etc library to make our module functional:


<?xml version="1.0"?>
 <config>
     <modules>
         <Aion_Customercategory>
             <version>0.1.0</version>
         </Aion_Customercategory>
     </modules>
     <global>
         <models>
             <aion_customercategory>
                 <class>Aion_Customercategory_Model</class>
                 <resourceModel>aion_customercategory_resource</resourceModel>
             </aion_customercategory>
             <aion_customercategory_resource>
                 <class>Aion_Customercategory_Model_Resource</class>
                 <entities>
                     <category>
                         <table>aion_customercategory_category</table>
                     </category>
                     <customer>
                         <table>aion_customercategory_customer</table>
                     </customer>
                 </entities>
             </aion_customercategory_resource>
         </models>
         <blocks>
             <aion_customercategory>
                 <class>Aion_Customercategory_Block</class>
             </aion_customercategory>
         </blocks>
         <helpers>
             <aion_customercategory>
                 <class>Aion_Customercategory_Helper</class>
             </aion_customercategory>
         </helpers>
         <resources>
             <aion_customercategory_setup>
                 <setup>
                     <class>Mage_Core_Model_Resource_Setup</class>
                     <module>Aion_Customercategory</module>
                 </setup>
             </aion_customercategory_setup>
         </resources>
     </global>
 </config>

After finishing with the basic tasks, we need to create a menu in the admin panel to handle the different items. In our case we create this in the Customer main menu so we insert the following into the config.xml:

 </global>

     <adminhtml>
         <menu>
             <customer>
                 <children>
                     <aion_customercategory>
                         <title>Customer Categories</title>
                         <sort_order>9999</sort_order>
                         <action>adminhtml/customercategory/index</action>
                     </aion_customercategory>
                 </children>
             </customer>
         </menu>
     </adminhtml>
 </config>

We can see that we need to set the XML structure in a way that we want to define a menu (<menu>) in the admin (<adminhtml>), where we create a sub menu (<children>) under Customers (<customer>) and where we give the following settings: :

  • title – title of the menu
  • sort order – in which line should the menu be located
  • action – which method of which controller should be run when clicking on the menu

Now if we take a look at the admin panel, we can see the following (it is very important to empty the Magento cache after every modification in xml, so that fresh data are reloaded):

Magento Admin Grid Customization

Now we need to script the controller to address the call: We need to create a class/method pair in the xml as an action under the controllers library (every ending of every controller is Controller and all the methods need to have endings of Action). Since it will be an admin controller, it should be derived from the Mage_Adminhtml_Controller_Action parent class. controllers/Adminhtml/CustomercategoryController.php

class Aion_Customercategory_Adminhtml_CustomercategoryController
    extends Mage_Adminhtml_Controller_Action
 {
     public function indexAction()
     {
         die('ok');
     }
 }

 

Next we need to “tell” Magento to use our controllers by inserting the following data in the config.xml:

</adminhtml>
<admin>  
    <routers> 
        <adminhtml> 
            <args>  
                <modules> 
                    <Aion_Customercategory before="Mage_Adminhtml">Aion_Customercategory_Adminhtml</Aion_Customercategory>  
                </modules>     
            </args>    
        </adminhtml> 
    </routers>
</admin>

So it should handle the paths we defined before handling the Magento router. The after item is also an option (in our case it is irrelevant). However, if we want to use a system url, then before is the suitable property, so that our controller/action pair is run before default (after the changes were made, we need to empty the cache so that Magento can read the modifications). Now our method can be run, but we need to define the layout in order to set what form it should take after rendering. This should also be given in xml format including the following steps.

  • We need to define in the config.xml, under the adminhtml tag, those layout.xml files that we want to use:

...
</menu>
     <layout>
         <updates>
             <aion_customercategory>
                 <file>aion/customercategory.xml</file>
             </aion_customercategory>
         </updates>
     </layout>
 </adminhtml>
...

  • We need to load the layout in the indexAction and then render it out

class Aion_Customercategory_Adminhtml_CustomercategoryController
    extends Mage_Adminhtml_Controller_Action
 {
     public function indexAction()
     {
         $this->loadLayout();
         $this->renderLayout();
     }
 }

  • We need to create these under app/design/adminhtml/default/default/layout (which means that the describing file of our module’s layout xml will be /app/design/adminhtml/default/default/layout/aion/customercategory.xml).
     

<?xml version="1.0"?>
 <layout version="0.1.0">
     <adminhtml_customercategory_index>
         <reference name="menu">
             <action method="setActive">
                 <menupath>customer/customercategory</menupath>
             </action>
         </reference>
         <reference name="content">
             <block
                type="aion_customercategory/adminhtml_category"
                name="category_index"
            />
         </reference>
     </adminhtml_customercategory_index>
 </layout>

In the above example we define the layout of the adminhtml/customercategory/index (<adminhtml_customercategory_index>)path where we set that the selected menu customer/customercategory and its content will be an adminhtml/category block created by us.

Creating an index block

Naturally, when a user clicks on our menu, he or she would like to see a list with the items he or she selected, as well as would like to edit / create / delete items. In the above layout we set that our block will be the adminhtml_category, thus we need to create the app/code/local/Aion/Customercategory/Block/Adminhtml/Category.php, which manages the content given in the layout:

class Aion_Customercategory_Block_Adminhtml_Category

extends Mage_Adminhtml_Block_Widget_Grid_Container
{

public function __construct()
{
$this->_blockGroup = 'aion_customercategory';
$this->_controller = 'adminhtml_category';

parent::__construct();

$this->_headerText = $this->__('Customer Categories');
$this->_addButtonLabel = $this->__('Add New');

}
}

We can see that our class is derived from Mage_Adminhtml_Block_Widget_Grid_Container because here we want to display a grid later on. Within the constructor we need to set the _blockGroup and the _controller by which we tell Magento to create our grid block from Aion_Customercategory_Block_Adminhtml_Category_Grid. When the parent class renders out the layout, the following function is run:  

$this
->getLayout()
->createBlock(
$this->_blockGroup.'/' . $this->_controller . '_grid',
$this->_controller . '.grid'
)

Grid block

We need to create the grid class set above under app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Grid.php:

class Aion_Customercategory_Block_Adminhtml_Category_Grid
extends Mage_Adminhtml_Block_Widget_Grid
 {
public function __construct()
 {
 parent::__construct();
 }
protected function _prepareCollection()
 {
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 return parent::_prepareColumns();
 }
 }

Our class derives from Mage_Adminhtml_Block_Widget_Grid, however we need to overwrite 3 methods so that it can function in our own module:

  • __construct – We need to set certain basic settings when creating instance
  • _prepareCollection – Defines where to call collection from
  • _prepareColumns – Defines what type of columns should be in the grid

The class, comprising our solutions, looks like this:

class Aion_Customercategory_Block_Adminhtml_Category_Grid extends Mage_Adminhtml_Block_Widget_Grid
 {
public function __construct()
 {
 parent::__construct();
 $this->setId('category_id');
 $this->setDefaultSort('category_id');
 $this->setDefaultDir('asc');
 $this->setSaveParametersInSession(true);
 }
protected function _prepareCollection()
 {
 $collection = Mage::getModel('aion_customercategory/category')
->getCollection();
 $this->setCollection($collection);
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 $this->addColumn(
 'category_id',
 array(
 'header' => $this->__('ID'),
 'align'  => 'right',
 'width'  => '50px',
 'type'   => 'number',
 'index'  => 'category_id',
 )
 );
$this->addColumn(
 'name',
 array(
 'header' => $this->__('Name'),
 'index'  => 'name',
 )
 );
$this->addColumn(
 'active',
 array(
 'header'   => $this->__('Active'),
 'index'    => 'active',
 'type'     => 'options',
 'options'  => array(
 1 => Mage::helper('adminhtml')->__('Yes'),
 0 => Mage::helper('adminhtml')->__('No')
 ),
 )
 );
return parent::_prepareColumns();
 }
 }

After implementing this code, the following appears:

Magento Admin Grid Modification

We can see that we have a grid which needs to be completed.

Creating a form

Along with fulfilling the planning/implementing criteria, it is recommended to manage the “creating new” and “edit existing” forms with a common class. Thus the following modifications should be made in the controller:

class Aion_Customercategory_Adminhtml_CustomercategoryController
extends Mage_Adminhtml_Controller_Action
 {
...
 public function editAction()
 {
 $id    = $this->getRequest()->getParam('id');
 $model = Mage::getModel('aion_customercategory/category');
if ($id) {
 $model->load($id);
 if (!$model->getId()) {
 $this->_getSession()->addError(
 $this->__('This Category no longer exists.')
 );
 $this->_redirect('*/*/');
 return;
 }
 }
$data = $this->_getSession()->getFormData(true);
 if (!empty($data)) {
 $model->setData($data);
 }
Mage::register('current_model', $model);
$this
 ->loadLayout()
 ->renderLayout();
 }
 public function newAction()
 {
 $this->_forward('edit');
 }
...
 }

So, when creating new, it should redirect to Edit and is validated there. If there is an ID parameter, it loads the necessary data. Just like it was needed in case of the indexAction, here too it should be defined in the layout xml how the page is built up:

<?xml version="1.0"?>
 <layout version="0.1.0">
...
<adminhtml_customercategory_edit>
 <update handle="editor"/>
 <reference name="menu">
 <action method="setActive">
 <menupath>customer/customercategory</menupath>
 </action>
 </reference>
 <reference name="content">
 <block
type="aion_customercategory/adminhtml_category_edit"
name="category_edit"
/>
 </reference>
 <reference name="left">
 <block
type="aion_customercategory/adminhtml_category_edit_tabs"
name="category_tabs"
/>
 </reference>
 </adminhtml_customercategory_edit>
...
 </layout>

In the edit panel the content will be the category editor (reference name=”content”), on the left hand side it will be the tabs (reference name=”left”). In order to make something appear on the page, these two blocks should be created. The first block is adminhtml_category_edit, which we need to define under app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit.php:

class Aion_Customercategory_Block_Adminhtml_Category_Edit
extends Mage_Adminhtml_Block_Widget_Form_Container
 {
 protected function _getHelper()
 {
 return Mage::helper('aion_customercategory');
 }
public function __construct()
 {
 parent::__construct();
 $this->_blockGroup = 'aion_customercategory';
 $this->_controller = 'adminhtml_category';
 $this->_mode       = 'edit';
 $this->_updateButton(
'save',
'label',
$this->_getHelper()->__('Save Category')
);
 $this->_addButton(
'saveandcontinue',
array(
 'label'   => $this->_getHelper()->__(
'Save and Continue Edit'
),
 'onclick' => 'saveAndContinueEdit()',
 'class'   => 'save',
 ), -100);
$this->_formScripts[] = "
 function saveAndContinueEdit(){
 editForm.submit($('edit_form').action+'back/edit/');
 }
 ";
 }
 }

This is a Mage_Adminhtml_Block_Widget_Form_Container, and the form itself, based on the _blockGroup, _controller, _mode, is loaded from the class Aion_Customercategory_Adminhtml_Category_Edit_Form. We create this class in the app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Form.php file:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Form
extends Mage_Adminhtml_Block_Widget_Form
 {
 protected function _prepareForm()
 {
 $form = new Varien_Data_Form(array(
 'id'      => 'edit_form',
 'method'  => 'post',
 'enctype' => 'multipart/form-data',
 'action'  => $this->getUrl('*/*/save'),
 )
 );
 $form->setUseContainer(true);
 $this->setForm($form);
 return parent::_prepareForm();
 }
 }

We have an empty form, now we need to create the class managing the tabs. We set the adminhtml_category_edit_tabs for the block in the layout.xml, therefore the name of the class should be Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs. Thus the file should be app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Tabs.php:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs
extends Mage_Adminhtml_Block_Widget_Tabs
 {
 public function __construct()
 {
 parent::__construct();
 $this->setId('category_tabs');
 $this->setDestElementId('edit_form');
 $this->setTitle(
Mage::helper('aion_customercategory')->__('Details')
);
 }
protected function _beforeToHtml()
 {
 $this->addTab(
 'category_section',
 array(
 'label' => Mage::helper('aion_customercategory')
->__('Category'),
 'title' => Mage::helper('aion_customercategory')
->__('Category'),
 'content' => $this->getLayout()
->createBlock(
'aion_customercategory/adminhtml_category_edit_category_form'
)->toHtml(),
 )
 );
 return parent::_beforeToHtml();
 }
 }

We can see that it can be set with the __consturctor what the identification will be, into which forms items will be loaded or what titles are seen above the tabs. We set the tabs dynamically in the _beforeToHtml, so the number of addTab() present here will be the number on the list as well. Now we need to create the adminhtml_category_edit_category_form block, which denotes the Aion_Customercategory_Block_Adminhtml_Category_Edit_Category_Form class, which will be in the app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Category/Form.php file:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Category_Form
extends Mage_Adminhtml_Block_Widget_Form
 {
 protected function _getModel()
 {
 return Mage::registry('current_model');
 }
protected function _getHelper()
 {
 return Mage::helper('aion_customercategory');
 }
protected function _prepareForm()
 {
 $model = $this->_getModel();
 $form  = new Varien_Data_Form(array(
 'id'     => 'edit_form',
 'action' => $this->getUrl('*/*/save'),
 'method' => 'post'
 ));
$fieldset = $form->addFieldset('base_fieldset', array(
 'legend' => $this->_getHelper()->__('Category Information'),
 'class'  => 'fieldset-wide',
 ));
if ($model && $model->getId()) {
 $modelPk = $model->getResource()->getIdFieldName();
 $fieldset->addField($modelPk, 'hidden', array(
 'name' => $modelPk,
 ));
 }
$fieldset->addField('name', 'text', array(
 'name'     => 'name', 'required' => true,
 'label'    => $this->_getHelper()->__('Name'),
 'title'    => $this->_getHelper()->__('Name'),
 )
 );
$fieldset->addField('active', 'select', array(
 'name'     => 'active', 'required' => true,
 'label'    => $this->_getHelper()->__('Active'),
 'title'    => $this->_getHelper()->__('Active'),
 'options'  => array(
 '1' => Mage::helper('adminhtml')->__('Yes'),
 '0' => Mage::helper('adminhtml')->__('No'),
 ),
 )
 );
if ($model) {
 $form->setValues($model->getData());
 }
 $this->setForm($form);
return parent::_prepareForm();
 }
 }

Besides the auxiliary methods we add the necessary fields to the panel in the _prepareForm() function. We then see a panel if we take a look at the page:

Magento Custom Admin Form Development

Thanks to this, we have created the panel of “creating new” and “edit item”, but we also need to save the data entered in the form. For this we need to define a saveAction() method in the controller (CustomercategoryController):

...
public function saveAction()
 {
 $redirectBack = $this->getRequest()->getParam('back', false);
 if ($data = $this->getRequest()->getPost()) {
 $id    = $this->getRequest()->getParam('id');
 $model = Mage::getModel('aion_customercategory/category');
 if ($id) {
 $model->load($id);
 if (!$model->getId()) {
 $this->_getSession()->addError(
 $this->__('This Category no longer exists.')
 );
 $this->_redirect('*/*/index');
 return;
 }
 }
try {
 $model->addData($data);
 $this->_getSession()->setFormData($data);
 $model->save();
 $this->_getSession()->setFormData(false);
 $this->_getSession()->addSuccess(
 $this->__('The Category has been saved.')
 );
 } catch (Mage_Core_Exception $e) {
 $this->_getSession()->addError($e->getMessage());
 $redirectBack = true;
 } catch (Exception $e) {
 $this->_getSession()->addError(
$this->__('Unable to save the Category.')
);
 $redirectBack = true;
 Mage::logException($e);
 }
if ($redirectBack) {
 $this->_redirect('*/*/edit', array('id' => $model->getId()));
 return;
 }
 }
 $this->_redirect('*/*/index');
 }
...

After saving, we find the items in the list that we entered:

Magento custom admin form save data

The general grid functions are already operational because of the class derived from Magento:

  • filtering
  • ordering
  • paging

We need to set what url should be activated if we click on a certain line. We do this in the Aion_Customercategory_Block_Adminhtml_Category_Grid class with the help of the following method:

public function getRowUrl($row)
 {
 return $this->getUrl('*/*/edit', array('id' => $row->getId()));
 }

In order to accomplish full functionality, deleting is also necessary, which can be reached from the editing form. After a confirmation pop-up it automatically redirects to the deleteAction() function. Therefore we need to implement that as well in the controller:

public function deleteAction()
 {
 if ($id = $this->getRequest()->getParam('id')) {
 try {
 $model = Mage::getModel('aion_customercategory/category');
 $model->load($id);
 if (!$model->getId()) {
 Mage::throwException($this->__('Unable to find a Category to delete.'));
 }
 $model->delete();
 $this->_getSession()->addSuccess(
 $this->__('The Category has been deleted.')
 );
 $this->_redirect('*/*/index');
 return;
 } catch (Mage_Core_Exception $e) {
 $this->_getSession()->addError($e->getMessage());
 } catch (Exception $e) {
 $this->_getSession()->addError(
 $this->__('An error occurred while deleting Category data. Please review log and try again.')
 );
 Mage::logException($e);
 }
 $this->_redirect('*/*/edit', array('id' => $id));
 return;
 }
 $this->_getSession()->addError(
 $this->__('Unable to find a Category to delete.')
 );
 $this->_redirect('*/*/index');
 }
 

Thanks to this, we can edit the categories. If we want to change the structure, we simply need to remove or add fields in the form by deleting or using the addField() method respectively. In order to be able to add customers to the categories in the planning phase, the ajax tab solution is recommended. A new tab should be added in the Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs class, which loads in the customer grid in an “ajax-based” way.  

protected function _beforeToHtml()
 {
 ...
 $this->addTab('customers', array(
 'label'     => $this->__('Customers'),
 'title'     => $this->__('Customers'),
 'url'       => $this->getUrl(
'*/*/customerstab',
array('_current' => true)
),
 'class'     => 'ajax'
 ));
return parent::_beforeToHtml();
 }

It can be seen under the url key that our controller’s customerstabAction method is called, in which we set the necessary data:

public function customerstabAction()
 {
 $saved_customer_ids = array();
 //Your load logic
 $this
 ->loadLayout()
 ->getLayout()
 ->getBlock('category.customer.tab')
 ->setSelectedCustomers($saved_customer_ids);
$this->renderLayout();
 }

It is important, however, that, as with all admin items, this too needs a descriptive block about what it should include:

...
<adminhtml_customercategory_customerstab>
 <block type="core/text_list" name="root" output="toHtml">
 <block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
</block>
 </adminhtml_customercategory_customerstab>
...

We need to create the block necessary, which is the following in our example: adminhtml_category_edit_customer_grid -> Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid -> app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Customer/Grid.php As he have already executed this with our previous grid, the __construct(), _prepareCollection(), _prepareColumns() methods should be set here as well:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid extends Mage_Adminhtml_Block_Widget_Grid
 {
 public function __construct()
 {
 parent::__construct();
 $this->setId('customerGrid');
 $this->setUseAjax(true);
 $this->setSaveParametersInSession(true);
 }
protected function _prepareCollection()
 {
 $collection = Mage::getResourceModel('customer/customer_collection')
 ->addNameToSelect();
$this->setCollection($collection);
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 $this->addColumn('selected_customers', array(
 'header'    => $this->__('Select'),
 'type'      => 'checkbox',
 'index'     => 'entity_id',
 'align'     => 'center',
 'field_name'=> 'selected_customers[]',
 'values'    => $this->getSelectedCustomers(),
 ));
$this->addColumn('customer_name', array(
 'header'    => $this->__('Name'),
 'index'     => 'name',
 'align'     => 'left',
 ));
$this->addColumn('email', array(
 'header'    => $this->__('E-mail'),
 'index'     => 'email',
 'align'     => 'left',
 ));
return parent::_prepareColumns();
 }
 }

If the constructor contains setUseAjax(true), then it will read the grid in an “ajax-based” way, however, the gridUrl should also be given in this class:

public function getGridUrl()
 {
 return $this->getUrl('*/*/customersgrid', array('_current' => true));
 }

This, logically, is a controller/action call, therefore this should be managed in the controller as well:

public function customersgridAction()
 {
 $this
 ->loadLayout()
 ->getLayout()
 ->getBlock('category.customer.tab')
 ->setSelectedCustomers(
$this->getRequest()->getPost('customers', null)
);
$this->renderLayout();
 }

In order to render out this “ajax-based” page without any errors, we need to give the following data in the layout.xml:

...
<adminhtml_customercategory_customersgrid>
<block type="core/text_list" name="root" output="toHtml">
<block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
</block>
</adminhtml_customercategory_customersgrid>
...

When we have finished with this, we can see our “ajax-based” customer grid under a tab:

Magento Admin Customer Grid

The next step is to store the selected customer entities in the given category, which means we need to put the IDs in a hidden input, so that these would be stored after saving. This is necessary because if we checked only the checkboxes selected in the grid, then after paging or filtering, the selected checkbox may not appear anymore and thus we could not include it as a part of the saving process. Thankfully, Magento provides a solution to this problem. We need to complement our tab with a new block (widget_grid_serializer), which manages this, so we only have to make some major settings and modifications in our code:

...
<adminhtml_customercategory_customerstab>
<block type="core/text_list" name="root" output="toHtml">
<block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
<block type="adminhtml/widget_grid_serializer" name="category.customer.serializer">
<action method="initSerializerBlock">
<grid_block_name>category.customer.tab</grid_block_name>
<data_callback>getSelectedCustomerIds</data_callback>
<hidden_input_name>customer_ids</hidden_input_name>
<reload_param_name>customers</reload_param_name>
</action>
</block>
    </block>
</adminhtml_customercategory_customerstab>
...

It can be seen that it calls the initSerializerBlock() method, which has the following parameters:

  • grid_block_name – defines wich grid the serialization applies to
  • data_callback – defines which created method returns the already selected IDs
  • hidden_input_name – defines the name of the input (we will refer with this name to the specific html tag later in the post)
  • reload_param_name – defines the parameter that contains the already stored items

Now we need to create the getSelectedCustomerIds() function in the Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid class, which returns the identifiers set in the category:

public function getSelectedCustomerIds()
 {
 $returnArray = array();
 //Your getter logic
 return $returnArray;
 }

With this, loading has been effectuated, now the saving process should be completed with the storing of the selected identifiers. It is important that the data in the input are serialized so we need to decrypt to be able to store them. It should be carried out within CustomercategoryController saveAction():

public function saveAction()
 {
 $redirectBack = $this->getRequest()->getParam('back', false);
 if ($data = $this->getRequest()->getPost()) {
 ...
if ($customersIds = $this->getRequest()->getParam('customer_ids', null)) {
 $customersIds = Mage::helper('adminhtml/js')
->decodeGridSerializedInput($customersIds);
 //Your save logic
 }
if ($redirectBack) {
 $this->_redirect('*/*/edit', array('id' => $model->getId()));
 return;
 }
 }
 ...
}

Finally, there is one more thing needed to have a fully functional panel. This is the function with wich the user can filter the customers in the list that are already selected as well as those that are not.

Magento Admin Customer Grid Filter

This can be solved by adding a method to the Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid , which we can set when adding a column, indicating what the filter function should be:

$this->addColumn('selected_customers', array(
 ...
'filter_condition_callback' => array($this, '_selectedCustomerFilter'),
 ));

I.e. filtering should be done by the _selectedCustomerFilter() in the selected_customers column:

protected function _selectedCustomerFilter($collection, $column)
 {
 $filter = $this->getRequest()->getParam('filter', null);
 $filter_data = Mage::helper('adminhtml')->prepareFilterString($filter);
 if (!$filter_data || !isset($filter_data['selected_customers'])) {
 return $this;
 }
$selectedCustomers = $this->getSelectedCustomers();
 if (!$selectedCustomers || !is_array($selectedCustomers)) {
 return $this;
 }
if ($filter_data['selected_customers'] == '1') {
 $operator = 'IN';
 } else {
 $operator = 'NOT IN';
 }
 $collection->getSelect()->where('e.entity_id ' . $operator . ' (' . implode(',', $selectedCustomers) . ')');
return $this;
 }

 

Summary

This example has shown us that we can easily satisfy such client needs when a new menu, a new form, a new grid or dynamic tabs should be created. We can also use existing collections or custom database models. Magento provides a tremendous amount of help throughout the process, it holds many core classes that we only need to either implement or complement. If you think this article can be useful to other readers as well, please share it. You can also write your comments below. If you have any queries, please don’t hesitate to comment.

 

Tamás Perencz-Szklenár

Tamás Perencz-Szklenár

Lead Backend Developer

Tamás is a PHP backend developer who likes broadening the scope of his expertise in complex projects. Car racing, either simulated or real, is in his blood.


NEED A RELIABLE, PROFESSIONAL MAGENTO DEVELOPMENT PARTNER?

Contact us if you have any question or requirement related to the preparation of a new or renewal of an existing online store.

magento_request
Do you need our support?
  • Magento Site Check
  • Magento Code Audit
  • Magento SEO Audit
  • Magento Project Rescue
Request help
Next