Problem: your client wants very specific urls(i.e. no module/action/key/value in the url) for seo purposes and also wants ALOT of them and wants to be able to edit them. So basically you want to have urls (routes) stored in a database so you can dynamically change and update them.
Well in order to get symfony to update the routes you either have to A) Write the new routes to the routing.yml B) Use php in the routes.yml to load the routes from the db or C) Somehow get symfony to load the routes via php very earyly on during page load. Either way you then need to get symfony to clear the cache so that the new routes are cached for production environment.
Symfony does now have support for php in yaml files, but the docs seem to indicate that since the config files are loaded so early you might not have access to advance symfony features (like DB access). I also wanted to keep my routes.yml clean and readable, reserved just the routes that have to be configured by hand. My application was going to have over a hundred dynamically generated routes, so I didn't want them cluttering up my config files. I decided to go with the third option, but it still may be possible to get routes.yml with php code embeded in it to do a better job.
I began reading up on the docs and poking around in the source code. The docs actually had some code examples that were wrong so I figured out most of this by walking through the source code. The relevant docs can be found at: http://www.symfony-project.org/book/1_2/17-Extending-Symfony. Besure to read the parts about Event listeners.
The solution I came up with involves modifying the frontendConfiguration.class.php to add a listener on routing.load_configuration event. Basically after symfony loads the routes via the routing.yml file then my registerRoutes::listenToRoutingLoadConfigurationEvent function gets called. Here is how the source code looks:
frontendConfiguration.class.php:
class registerRoutes { static public function listenToRoutingLoadConfigurationEvent(sfEvent $event) { $routing = $event->getSubject(); $categories=Doctrine::getTable('Category')->findAll(); $products=Doctrine::getTable('Product')->findAll(); foreach($products as $i=>$product){ if(strlen($product->route)){ //print "adding route: ".$product->route."<br/>"; $name= 'product_'.$product->id; $route=new sfRoute($product->route, array('module' => 'browse', 'action' => 'catalog', 'product'=>$product->id), array('product'=>'\d+'), array('extra_parameters_as_query_string'=>false) ); $routing->prependRoute( $name, $route); } } foreach($categories as $i=>$category){ if(strlen($category->route)){ //print "adding route: ".$category->route."<br/>"; $name= 'category_'.$category->id; $line= $route=new sfRoute($category->route, array('module' => 'browse', 'action' => 'catalog', 'category'=>$category->id), array('catagory'=>'\d+'),array('extra_parameters_as_query_string'=>false) ); $routing->prependRoute( $name, $route); } } } } class frontendConfiguration extends sfApplicationConfiguration { public function configure() { $this->dispatcher->connect('routing.load_configuration', array('registerRoutes', 'listenToRoutingLoadConfigurationEvent')); } }
As you can see we set a listener on route.load_configuration, and the listener calls registerRoutes::listenToRoutingLoadConfigurationEvent which I decided to just define in the same file for convenience. The registerRoutes() function basically just grabs the routing object (which is passed to the function from symfony) and then loads the routes that are saved in the db. I used prependRoute here so that means the most important routes need to be loaded last.
The last thing you will want to do is clear the cache whenever the routes in the db get updated. In my site the routes are changed via the backend app so that just makes it slightly more complicated. Here's how you clear the frontend cache via the backend:
$frontend_cache_dir = sfConfig::get('sf_cache_dir').DIRECTORY_SEPARATOR.'frontend'; $cache = new sfFileCache(array('cache_dir' => $frontend_cache_dir)); $cache->clean();
I seem to remeber that the docs had the sf_cache_dir variable wrong and I had to dump the sfConfig object in order to find the right variable name.
Well thats it. Be sure to check out the google symfony groups and feel free to email us if you need some professional help with your symfony web site!
|