1. Introduction
    1. About Agavi
    2. MVC in Agavi
    3. Overview of Agavi
    4. Overview of Application Execution Flow
    5. A Word About Actions
    6. Application filesystem layout
    7. Overview of application configuration
  2. Setting Up The Initial Application
    1. Installing Agavi
    2. Creating an Agavi Project
    3. Finishing The Setup
    4. Finishing The Basic Setup
    5. Installing a New Copy of Your Application
  3. Adding First Code
    1. Creating A New module
    2. Creating A New Action
    3. Tying Things Together — An Introduction To Routing
    4. Fixing The Bloggie Routing
    5. Accessing Request Parameters and Validation Basics
    6. Handling Validation Errors
  4. Putting The M in MVC
    1. Creating A New Model
    2. Adapting The Actions and Views
    3. Custom Validators
  5. Polishing It Up
    1. Layers and Layouts
    2. Applying Our Layout
    3. What Are Slots?
    4. Adding The Post's Title To The URL
    5. Routing Callbacks
    6. Using Callbacks for the Title in URLs
  6. Connecting to a database
    1. The Database Manager
  7. Handling Output Variants
    1. Output Types
    2. Exception Templates
    3. Generating an RSS Feed
  8. Form Processing
    1. Adding a Post
    2. Editing a Post
    3. The Form Population Filter (FPF)
  9. Creating a User Authentication System
  10. Adding To The Master Template

Using Callbacks for the Title in URLs

We can use the onGenerate method from AgaviRoutingCallback to factor out the code needed to generate a URL for the post's detail page. We'll just pass the PostModel in and the callback takes care of extracting the id and the title for the URL. Let's create a callback in app/modules/Posts/lib/routing/PostRoutingCallback.class.php.

<?php

class PostRoutingCallback extends AgaviRoutingCallback
{
  /**
   * Gets executed when the route of this callback is about to be reverse 
   * generated into an URL.
   *
   * @param      array The default parameters stored in the route.
   * @param      array The parameters the user supplied to AgaviRouting::gen().
   * @param      array The options the user supplied to AgaviRouting::gen().
   *
   * @return     bool  Whether this route part should be generated.
   */
  public function onGenerate(array $defaultParameters, array &$userParameters, array &$userOptions)
  {
    $post = $userParameters['post']->getValue();
    
    $routing = $this->getContext()->getRouting();
    
    $userParameters['post'] = $routing->createValue($post->getId());
    $userParameters['title'] = $routing->createValue(preg_replace('/\W/', '-', $post->getTitle()));
    
    return true;
  }
}

?>

The callback must be available in the global autoload file in app/config/autoload.xml:

<?xml version="1.0" encoding="UTF-8"?>
  <ae:configurations xmlns="http://agavi.org/agavi/config/parts/autoload/1.0" xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0" parent="%core.system_config_dir%/autoload.xml">
    <ae:configuration>

      <autoload name="BlogBaseAction">%core.lib_dir%/action/BlogBaseAction.class.php</autoload>
      <autoload name="BlogBaseModel">%core.lib_dir%/model/BlogBaseModel.class.php</autoload>
      <autoload name="BlogBaseView">%core.lib_dir%/view/BlogBaseView.class.php</autoload>
      <autoload name="PostRoutingCallback">%core.module_dir%/Posts/lib/routing/PostRoutingCallback.class.php</autoload>

    </ae:configuration>
  </ae:configurations>

and registered in app/config/routing.xml:

<?xml version="1.0" encoding="UTF-8"?>
  <ae:configurations xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0" xmlns="http://agavi.org/agavi/config/parts/routing/1.0">
    <ae:configuration>
      <routes>

        <!-- default action for "/" -->
        <route name="index" pattern="^/$" module="Posts" action="%actions.default_action%" />

        <route name="posts" pattern="^/posts" module="Posts">
          <route name=".post" pattern="^/(post:\d+)(-{title:[-\w]+})?" action="Post">
            <callbacks>
              <callback class="PostRoutingCallback" />
            </callbacks>
            <route name=".show" pattern="^$" action=".Show" />
          </route> 
        </route>

      </routes>
    </ae:configuration>
  </ae:configurations>

Now we need to pass a PostModel instead of id and title in our ShowSuccessView [app/modules/Posts/views/Posts/ShowSuccess.class.php]:

<?php

class Posts_Post_ShowSuccessView extends BlogPostsBaseView
{
  public function executeHtml(AgaviRequestDataHolder $rd)
  {
    $this->setupHtml($rd);
    
    $p = $this->getAttribute('post');
    $post = $p->toArray();
    $post['url'] = $this->getContext()->getRouting()->gen('posts.post.show', array('post' => $p));
    
    $this->setAttribute('post', $post);
    
    $isList = $this->getContainer()->getParameter('is_slot', false);
    
    if($isList) {
      $headlineSize = 2;
      $linkHeadline = true;
      $displayComments = false;
    } else {
      $headlineSize = 1;
      $linkHeadline = false;
      $displayComments = true;
    }
    
    $this->setAttribute('headline_size', $headlineSize);
    $this->setAttribute('link_headline', $linkHeadline);
    $this->setAttribute('display_comments', $displayComments);
    
    $this->setAttribute('_title', $post['title']);
  }
}
?>

and that's it. Now the no part of the application itself needs to know about how the urls to the posts are contructed and which parameters are required. That's all nicely encapsulated in the routing.