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.