Pages

Friday, October 16, 2009

Using jQuery's Flexigrid Plugin with Zend

There is no ready grid component in Zend Framework. Most of the forums recommend implementation using Dojo's grid tool, but since I'm new to both Zend Framework and Dojo, this didn't seem like all that great of an idea.

However, I have made frequent use of Flexigrid in the past and after making a half-hearted attempt at using Dojo, I decided that I might as well use Flexigrid -- at least I'm already familiar with how it works.

Now, none of this was obvious at first. Sure, I understand all of the principals of Zend Framework's MVC implementation, but figuring out where to put stuff is a real challenge.

This demonstration is meant to show how to get started. You'll want to make this more programmatic later, but hopefully it gets you to a working implementation.

AJAX Action

First, I would recommend creating the action handler for the AJAX call. You can test this to make sure it's the output expected by Flexigrid.

public function ajaxAction()
{
    $Table = new Model_User_Table();
    $total_rows = $Table->count(); # this is a custom method
    
    // Request parameters received via GET from flexigrid.
    $sort_column = $this->_getParam('sortname','name'); # this will default to undefined
    $sort_order = $this->_getParam('sortorder','asc'); # this will default to undefined
    $page = $this->_getParam('page',1);
    $limit = $this->_getParam('rp',10);
    $offset = (($page - 1) * $limit);
    $search_column = $this->_getParam('qtype');
    $search_for = $this->_getParam('query');
    
    // Build a query to get the results.
    $Select = $Table->select()->order("$sort_column $sort_order")->limit($limit,$offset);
    if (!empty($search_column) && !empty($search_for))
    {
        $Select->where($search_column.' LIKE ?','%'.$search_for.'%');
    }

    // Get the result rows using Zend's paginator.
    $Pager = Zend_Paginator::factory($Select);
    $Pager->setCurrentPageNumber($page);
    $Pager->setItemCountPerPage($limit);
    $rows = $Pager->getIterator();

    // Send headers.
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
    header("Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . "GMT" );
    header("Cache-Control: no-cache, must-revalidate" );
    header("Pragma: no-cache" );
    header("Content-type: text/xml");

    // Prep the XML.
    $xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?";
    $xml .= ">\n";
    $xml .= "<rows>";
    $xml .= "<page>$page</page>";
    $xml .= "<total>$total_rows</total<";
    foreach ($rows as $row)
    {   
        $xml .= sprintf('<row id="%s">',$row['id']);
        $xml .= sprintf('<cell<<![CDATA[%s]]<</cell<',$row['id']);
        $xml .= sprintf('<cell<<![CDATA[%s]]<</cell<',$row['name']);
        $xml .= sprintf('<cell<<![CDATA[%s]]<</cell<',$row['email']);
    }

    // Disable the default layout and output the XML.
    $this->getHelper('layout')->disableLayout();
    $this->getHelper('ViewRenderer')->setNoRender();
    $this->getResponse()->setBody($xml);
}

View Script

Next we'll look at the view script. In my app, the default action displays users in a grid, so in this case the view script was located at view/scripts/user/index.phtml and all this is where I put my javascript and table HTML (such as it is). The CSS and JavaScript should probably be abstracted into a Grid class along with the specification of rows and tables.

<style type="text/css">
<!--
.flexigrid div.fbutton .add { background: url(/eve/icons/small/plus.png) no-repeat center left; }
.flexigrid div.fbutton .delete { background: url(/eve/icons/small/subtract.png) no-repeat center left; }
.flexigrid div.fbutton .edit { background: url(/eve/icons/small/pencil.png) no-repeat center left; }
-->
</style>
<script type="text/javascript">
$(document).ready(function(){
    function add_record() { window.location='/user/add'; }
    
    function delete_record(command,grid)
    {
        var record_count = $('.trSelected',grid).length;
        if (0 == record_count)
        {
            alert('Please select a record first by clicking on the appropriate row.');
            return;
         }
         $('.trSelected',grid).each(function(){
             var id = this.id.substr(3);
             window.location = '/user/delete/id/'+id;
         });
     } // delete_record

     function edit_record(command,grid)
     {
         var record_count = $('.trSelected',grid).length;
         if (0 == record_count)
         {
             alert('Select a record first by clicking on the appropriate row.');
             return;
         }
         $('.trSelected',grid).each(function(){
             var id = this.id.substr(3);
             window.location = '/user/edit/id/'+id;
         });
     } // edit_record

    $('#users').flexigrid({
        url: '/user/ajax',
        dataType: "xml",
        usepager: true,
        useRp: true,
        rp: 10,
        sortname: 'name',
        sortorder: 'asc',
        buttons: [
            {name: 'Add', bclass: 'add', onpress: add_record},
            {name: 'Edit', bclass: 'edit', onpress: edit_record},
            {name: 'Delete', bclass: 'delete', onpress: delete_record}
        ],
        colModel: [
            {display: 'ID', name: 'id', width: 30, sortable: true, align: 'left'},
            {display: 'Name', name: 'name', width: 120, sortable: true, align: 'left'},
            {display: 'Email', name: 'email', width: 150, sortable: true, align: 'left'}
        ]
     });
});
</script>
<table id="users"></table>

Main Action

Last, change the indexAction() method of the controller.

$this->view->headScript()->appendFile('/scripts/jquery.js');
$this->view->headScript()->appendFile('/scripts/flexigrid/flexigrid.pack.js');
$this->view->appendStylesheet('/scripts/flexigrid/css/flexigrid/flexigrid.css');

1 comment:

  1. method count on that Model_User_Table may I know cause those script got error on that line

    ReplyDelete