Pages

Showing posts with label Zend. Show all posts
Showing posts with label Zend. Show all posts

Thursday, January 28, 2010

Error Handling Errors

I fixed a problem today where custom blog software had stopped working after a permissions update. A large number of sites are based upon the blog software and they were all down. Blank page. No errors on screen or in the logs.

This was not software and I came in knowing nothing about it. It might've helped that I do know the developer, but he was offline and unreachable, and no longer with the company for which the software was created. Panic was setting in with customer service.

It's really frustrating when error reporting is turned completely off and no other error handling has been implemented. I knew the problem must be permissions related but there was just no clue as to what file was failing to write. I also knew the application was Zend Framework, which I've been working with for about a year or so. A quick change to the application.ini ...

phpSettings.display_errors = 1

phpSettings.error_reporting = 1

... and I was to see the error and correct the permissions.

This brings to mind: The Log File is Your Friend. Frameworks or applications that implement their own error handling need to also find a way of silently indicating the problem. In this case, it could be done in a custom manner using Zend_Log or by adding log_errors to the php.ini file or to application.ini, like so:

phpSettings.error_reporting = 1024

phpSettings.log_errors = 1

Monday, November 30, 2009

Forcing a 404 Error with Zend Framework

There are many things I like about Zend Framework. And there are times when using it is like searching for a black cat in a dark room. Seemingly simple tasks end up frittering away time looking through the documentation and source code.

Inducing a 404 response is one of these things. I didn't want to simply redirect the page, but rather, I wanted the ErrorController to handle the error just like usual.

In your action controller:

// Some "not found" condition occurs.
throw new Zend_Controller_Action_Exception('Page not found.',404);

And in views/scripts/error/error.phtml:

<? if (404 == $this->exception->getCode()): ?>
// Put your 404 message here.
<? endif ?>

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');