Handling Validation Errors

So far we have only been concerned about how to keep users from passing invalid data but not about what happens if users passes invalid data - in our case calls an invalid URL to a post's detail page. If you try that now you'll see a shiny AgaviException explaining that it can't find a certain view's file. We'd rather see a "404 Not Found" page in that moment.

The handle*Error Methods

Whenever validation fails the framework calls a method on the action to handle the error. As with the execute* methods the framework first looks for a method named handle<RequestMethod>Error() and if no such method is declared it calls a generic handleError(). So if an error occurs on a Write request, agavi looks if handleWriteError() was declared, if validation fails on a Read request, it looks for handleReadError(). In both cases it would fall back to handleError() if no specific method was declared. If an action does not declare any error handling at all, the default handleError() method declared in AgaviAction is executed. The signature for all these methods is the same as for the execute() method, they accept an AgaviRequestDataHolder as single parameter and return a view name. Please refer to the API-documentation for AgaviAction::handleError() for further details.

In our case we'll rely on the standard method predefined in Agavi. It executes no logic and just returns 'Error' as the view's name. We don't have that view yet, so let's go create one.

Creating a new View

Creating a new view is most easily done using the agavi build system:

  
  bloggie$ dev/bin/agavi view-create

  Module name: Posts

  Action name: Post.Show

  View name: Error

  bloggie$

Forwarding to another Action

A validation failure in this specific case means that the requested post does not exist or is otherwise inaccessible, so instead of displaying an error message to the user we should display the 404-Not-Found page. We don't want to implement this page in all and every place where such errors occur and we alread have an implementation for it so all we need to do is tell Agavi to continue with the default 404 action. This is called a "forward" as the client never gets to know that the last executed action is not the action that was actually requested. It's important to differentiate a forward and a redirect, the latter one uses the HTTP 301/302 header to tell the client to go looking somewhere else for the requested ressource.

Forwards should happen in the view as the response may depend on the output type. To forward in a view, we just return a new AgaviExecutionContainer with the module and action we want to forward to. To create such a container we use the convienience method AgaviView::createForwardContainer(). It will create a new container based on the current view's container and initialize it properly. Please refer to the API-Documentation for further details on this method.

  <?php

  class Posts_Post_ShowErrorView extends BlogPostsBaseView
  {
    public function executeHtml(AgaviRequestDataHolder $rd)
    {
      return $this->createForwardContainer(AgaviConfig::get('actions.error404_module'), AgaviConfig::get('actions.error404_action'));
    }
  }

  ?>

The calls to AgaviConfig::get() just read the configured directives from the app/config/settings.xml and otherwise that's all we need to display the 404 page. Go ahead, have a look - enter an invalid post id and see how the right page is displayed. It even emits the right set of headers.