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.

