How to create a custom grid from scratch

inchoo orders

We received a request from a client where they wanted to implement an expanded order grid while still keeping the default Magento’s one. For this example we’ll create a new module named Inchoo Orders.

Step 1.

Create new module in etc/modules called Inchoo_Orders.xml

<?xml version="1.0"?>
 
<config>
    <modules>
        <Inchoo_Orders>
            <active>true</active>
            <codePool>community</codePool>
        </Inchoo_Orders>
    </modules>
</config>

Step 2.

Next thing we’ll do is create folder Inchoo/Orders inside app/code/community and inside we’ll make few folders: Block, controllers, etc, Helper.

inchoo_orders

Step 3.

Create config.xml inside etc folder.

<?xml version="1.0"?>
 
<config>
    <modules>
        <Inchoo_Orders>
            <version>0.0.0.1</version>
        </Inchoo_Orders>
    </modules>
 
    <global>
        <models>
            <inchoo_orders>
                <class>Inchoo_Orders_Model</class>
                <resourceModel>inchoo_orders_resource</resourceModel>
            </inchoo_orders>
        </models>
 
        <resources>
            <inchoo_orders_setup>
                <setup>
                    <module>Inchoo_Orders</module>
                </setup>
            </inchoo_orders_setup>
        </resources>
 
        <blocks>
            <inchoo_orders>
                <class>Inchoo_Orders_Block</class>
            </inchoo_orders>
        </blocks>
 
        <helpers>
            <inchoo_orders>
                <class>Inchoo_Orders_Helper</class>
            </inchoo_orders>
        </helpers>
    </global>
 
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <inchoo_orders before="Mage_Adminhtml">Inchoo_Orders_Adminhtml</inchoo_orders>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
</config>

Step 4

Create adminhtml.xml file inside etc folder which will add a link to our orders page in magento admin panel.

<?xml version="1.0"?>
 
<config>
    <menu>
        <sales>
            <children>
                <inchoo_orders translate="title" module="inchoo_orders">
                    <sort_order>10</sort_order>
                    <title>Orders - Inchoo</title>
                    <action>adminhtml/order/</action>
                </inchoo_orders>
            </children>
        </sales>
    </menu>
</config>

Step 5

Create blank helper class.

<?php
 
class Inchoo_Orders_Helper_Data extends Mage_Core_Helper_Abstract
{
 
}

Step 6

Next step is to create controller for our grid.

<?php
 
class Inchoo_Orders_Adminhtml_OrderController extends Mage_Adminhtml_Controller_Action
{
    public function indexAction()
    {
        $this->_title($this->__('Sales'))->_title($this->__('Orders Inchoo'));
        $this->loadLayout();
        $this->_setActiveMenu('sales/sales');
        $this->_addContent($this->getLayout()->createBlock('inchoo_orders/adminhtml_sales_order'));
        $this->renderLayout();
    }
 
    public function gridAction()
    {
        $this->loadLayout();
        $this->getResponse()->setBody(
            $this->getLayout()->createBlock('inchoo_orders/adminhtml_sales_order_grid')->toHtml()
        );
    }
 
    public function exportInchooCsvAction()
    {
        $fileName = 'orders_inchoo.csv';
        $grid = $this->getLayout()->createBlock('inchoo_orders/adminhtml_sales_order_grid');
        $this->_prepareDownloadResponse($fileName, $grid->getCsvFile());
    }
 
    public function exportInchooExcelAction()
    {
        $fileName = 'orders_inchoo.xml';
        $grid = $this->getLayout()->createBlock('inchoo_orders/adminhtml_sales_order_grid');
        $this->_prepareDownloadResponse($fileName, $grid->getExcelFile($fileName));
    }
}

Step 7

Next thing we do is create grid container in Block/Adminhtml/Sales/Order.php

<?php
 
class Inchoo_Orders_Block_Adminhtml_Sales_Order extends Mage_Adminhtml_Block_Widget_Grid_Container
{
    public function __construct()
    {
        $this->_blockGroup = 'inchoo_orders';
        $this->_controller = 'adminhtml_sales_order';
        $this->_headerText = Mage::helper('inchoo_orders')->__('Orders - Inchoo');
 
        parent::__construct();
        $this->_removeButton('add');
    }
}

Step 8

Last step is making grid class in Block/Adminhtml/Sales/Order/Grid.php

<?php
 
class Inchoo_Orders_Block_Adminhtml_Sales_Order_Grid extends Mage_Adminhtml_Block_Widget_Grid
{
    public function __construct()
    {
        parent::__construct();
        $this->setId('inchoo_order_grid');
        $this->setDefaultSort('increment_id');
        $this->setDefaultDir('DESC');
        $this->setSaveParametersInSession(true);
        $this->setUseAjax(true);
    }
 
    protected function _prepareCollection()
    {
        $collection = Mage::getResourceModel('sales/order_collection')
            ->join(array('a' => 'sales/order_address'), 'main_table.entity_id = a.parent_id AND a.address_type != \'billing\'', array(
                'city'       => 'city',
                'country_id' => 'country_id'
            ))
            ->join(array('c' => 'customer/customer_group'), 'main_table.customer_group_id = c.customer_group_id', array(
                'customer_group_code' => 'customer_group_code'
            ))
            ->addExpressionFieldToSelect(
                'fullname',
                'CONCAT({{customer_firstname}}, \' \', {{customer_lastname}})',
                array('customer_firstname' => 'main_table.customer_firstname', 'customer_lastname' => 'main_table.customer_lastname'))
            ->addExpressionFieldToSelect(
                'products',
                '(SELECT GROUP_CONCAT(\' \', x.name)
                    FROM sales_flat_order_item x
                    WHERE {{entity_id}} = x.order_id
                        AND x.product_type != \'configurable\')',
                array('entity_id' => 'main_table.entity_id')
            )
        ;
 
        $this->setCollection($collection);
        parent::_prepareCollection();
        return $this;
    }
 
    protected function _prepareColumns()
    {
        $helper = Mage::helper('inchoo_orders');
        $currency = (string) Mage::getStoreConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_BASE);
 
        $this->addColumn('increment_id', array(
            'header' => $helper->__('Order #'),
            'index'  => 'increment_id'
        ));
 
        $this->addColumn('purchased_on', array(
            'header' => $helper->__('Purchased On'),
            'type'   => 'datetime',
            'index'  => 'created_at'
        ));
 
        $this->addColumn('products', array(
            'header'       => $helper->__('Products Purchased'),
            'index'        => 'products',
            'filter_index' => '(SELECT GROUP_CONCAT(\' \', x.name) FROM sales_flat_order_item x WHERE main_table.entity_id = x.order_id AND x.product_type != \'configurable\')'
        ));
 
        $this->addColumn('fullname', array(
            'header'       => $helper->__('Name'),
            'index'        => 'fullname',
            'filter_index' => 'CONCAT(customer_firstname, \' \', customer_lastname)'
        ));
 
        $this->addColumn('city', array(
            'header' => $helper->__('City'),
            'index'  => 'city'
        ));
 
        $this->addColumn('country', array(
            'header'   => $helper->__('Country'),
            'index'    => 'country_id',
            'renderer' => 'adminhtml/widget_grid_column_renderer_country'
        ));
 
        $this->addColumn('customer_group', array(
            'header' => $helper->__('Customer Group'),
            'index'  => 'customer_group_code'
        ));
 
        $this->addColumn('grand_total', array(
            'header'        => $helper->__('Grand Total'),
            'index'         => 'grand_total',
            'type'          => 'currency',
            'currency_code' => $currency
        ));
 
        $this->addColumn('shipping_method', array(
            'header' => $helper->__('Shipping Method'),
            'index'  => 'shipping_description'
        ));
 
        $this->addColumn('order_status', array(
            'header'  => $helper->__('Status'),
            'index'   => 'status',
            'type'    => 'options',
            'options' => Mage::getSingleton('sales/order_config')->getStatuses(),
        ));
 
        $this->addExportType('*/*/exportInchooCsv', $helper->__('CSV'));
        $this->addExportType('*/*/exportInchooExcel', $helper->__('Excel XML'));
 
        return parent::_prepareColumns();
    }
 
    public function getGridUrl()
    {
        return $this->getUrl('*/*/grid', array('_current'=>true));
    }
}

Our new custom orders page can be accessed by going to Sales -> Orders – Inchoo in admin panel.

link in admin

End result should be something like this:

inchoo orders grid


75 comments

  1. I have followed all steps and Order – Inchoo sub menu is there in Sales menu but when I click on it, it leads to 404 Page Not found. could you tell what is the missing part.. I have checked it lots of time.

  2. what is the importance of this function..?
    public function gridAction()
    it never called

  3. Use this code to add ACL in adminhtml.xml

    <acl>
    <config>
           <!--- menu ---- -->
            <resources>
                <admin>
                    <children>
                        <sales translate="title" module="inchoo_orders">
                            <title>Sales</title>
                            <sort_order>1</sort_order>
                            <children>
                                <inchoo_orders>
                                    <title>Inchoo Orders Menu</title>
                                </inchoo_orders>
                            </children>
                        </sales>
                    </children>
                </admin>
            </resources>
        </acl>
    <config>
  4. How to make the row clickable , I mean, I used getrowurl for it but I want to make a custom sales_order/view block inside incho_orders module. how can i do that?

    1. So okay I found it. Would be nice if this was clearly mentioned in the tutorial.

      For those of you who were also looking for this, in case if you want to add several custom grids, or you just want to have it a more suitable name. You CAN change the url route for the grid.

      So for example if you want to change “adminhtml/order/” to “adminhtml/mygrid/”.

      In the adminhtml.xml change adminhtml/order/ to adminhtml/mygrid/

      Then change the file AND classname of the controller in controllers/Adminhtml/ from OrderController.php to MygridController.php (Yes you should put the controller in the Adminhtml subfolder for it to work, something they also didn’t mention in this tutorial)

      Of course make sure you change the class name of the controller as well. That’s it.

  5. hello my first question is where i can create helper class
    and second is contoller
    plz give path

  6. Its working fine only we have to do is please place our controller in adminhtml folder

  7. Change adminhtml.xml tag content like this :
    adminhtml/sales_order/
    This Will Work Fine

  8. Try to use filter on Name or Products Purchased. There is a problem with filter index on these 2 columns:

     $this->addColumn('products', array(
                'header'       => $helper->__('Products Purchased'),
                'index'        => 'products',
                'filter_index' => '(SELECT GROUP_CONCAT(\' \', x.name) FROM sales_flat_order_item x WHERE main_table.entity_id = x.order_id AND x.product_type != \'configurable\')'
            ));
    
            $this->addColumn('fullname', array(
                'header'       => $helper->__('Name'),
                'index'        => 'fullname',
                'filter_index' => 'CONCAT(customer_firstname, \' \', customer_lastname)'
            ));

    When filter_index value is added to MySQL query it is wrapped inside “, what makes SQL error.

    
    SELECT COUNT(*) FROM `sales_flat_order` AS `main_table`
      INNER JOIN `sales_flat_order_address` AS `a` ON main_table.entity_id = a.parent_id AND a.address_type != 'billing'
      INNER JOIN `customer_group` AS `c` ON main_table.customer_group_id = c.customer_group_id WHERE (`CONCAT(customer_firstname, ' ', customer_lastname)` LIKE '%Doe%')
    
    
    				
    1. I am getting same error as below:
      Unknown column ‘CONCAT(firstname, ‘ ‘, lastname)’ in ‘where clause’

      Please provide solution.

    2. Same problem with products filtering, solved with this (Grid.php):

      protected function _productsFilter($collection, $column)
      {
      if (!$value = $column->getFilter()->getValue()) {
      return $this;
      }

      $this->getCollection()->getSelect()->where(
      "(SELECT GROUP_CONCAT(' ', x.name) FROM sales_flat_order_item x WHERE x.product_type !='configurable' AND main_table.entity_id = x.order_id ) LIKE ?"
      , "%$value%");
      return $this;
      }

      in _prepareColumns method:

      $this->addColumn('products', array(
      'header' => $helper->__('Prodotti Acquistati'),
      'index' => 'products',
      'filter_condition_callback' => array($this, '_productsFilter')
      ));

  9. Hi, Thank You the code ..
    Please Help Me i got a error Call to a member function setSaveParametersInSession() .
    Please give a guidance for it ..

  10. Hi,
    I followed all steps you defined above.But when i click on “Order-Inchoo” it shows 404 error.why?

  11. I have followed all steps and Order – Inchoo sub menu is there in Sales menu but when I click on it, it leads to 404 Page Not found. could you tell what is the missing part.. I have checked it lots of time.

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.