CakePHP with Symfony’s2 router

Zzyzx_road

A couple of months ago I have started my adventure with CakePhp and as every Symfony’s developer I thought that any other framework except Symfony is a piece of crap. Day by day and step by step I began to realize that’s not so bad as it seemed to be in the beginning. Well, the second version of CakePhp still has a lot old-fashioned patterns, singletons or lack of tests, but I can live with that. I saw a lot of better or worse frameworks in my life. However, one module remains a bitter aftertaste – the router.

Let’s just look at the very basic things. Let’s start with new route declaration.

1
2
3
4
5
6
Router::connect ( '/blog/:slug', array (
        'slug' => 'index',
        'controller' => 'blog',
        'action' => 'post',
                'template' => 'white'
));

It looks quite natural and shouldn’t make any unpredictable problems… well, shouldn’t. When I wanted to display an URL to the blog/:slug page in a View file I was struggling with additionals parameters and not found paths. As every developer I opened the declaration of `connect` method and tried to figure out what exactly happens inside. First impression was quite good – `public static function connect($route, $defaults = array(), $options = array()); `.

The author of Clean Code – Robert C. Martin emphasizes on the right naming. All methods, functions, classes or variables should tell you, literally, what responsibility they have. So, when I saw three parameters in `connect` method they seemed obvious – `$route` was for the URL, `$defaults` – filled the missing arguments by default values and `$options` – about additional information for my route.

Everything was clear and I couldn’t find out why my code cannot compose a proper URL until I caught the condition which was commented:

// keys that exist in the defaults and have different values is a match failure.

I was so wrong. In the result I’m pretty sure and, of course, I confirmed that, the code `Html->link($postName, array(‘controller’ => ‘blog’, ‘action’ => ‘post’, ‘slug’ => $slug)); ?>` will break, because we didn’t pass the argument `template`.

I believe that the second parameter in `connect` method should be called `$requires`. One confusing name took me 4 hours of my live and caused a lot of problems.

So, what can I do with this?

Extend! Extend the original routing by something what you really like. I’m huge fan of YML routing implemented in Symfony 2. It’s really simple, transparent and easy to read. Moreover, and what is the most important thing for me, each route has a name. So I can build every single URL without passing all `defaults` parameters, but focus only on mandatory as `slug`.

1
2
3
blog_show:
    path:      /blog/{slug}
    defaults:  { controller: Blog, action: post }

Looks better, right? Inspired by a presentation of @jakub_zalasSymfony in the Wild I decided to start writing a bridge for routing between CakePHP and Symfony2 route component.

CakePhp Symfony Router

Ready to use Plugin can be found at https://github.com/piotrpasich/cakephp-symfony-routeror http://plugins.cakephp.org/p/1725-cakephp-symfony-router. Also, you can find there a exhaustive manual how to install and use this component. I have prepared a god base to eztend this for XML configuration files and annotations.

The main assumptions were to create a really simple in use system based on symfony router component to cover problems with I struggled. So, now you can call each route by name like this:

1
2
<?php echo $this->SymfonyRouter->getPath('blog_post', array('slug' => $post->getSlug())); ?>
<a href="<?php echo $this->SymfonyRouter->getPath('blog_post', array('slug' => $post->getSlug())); ?>"><?php echo $post->getSlug() ?></a>

Conclusion

I really like the wise spectrum of available open source frameworks and platforms on the market. I learned Symfony, Zend, Kohana, CakePhp or even Drupal or WordPress. However, besides of the naming convention, used architecture or design patterns which can be more or less comfortable for me, sometimes I have to face a really huge problem which stops me for a couple of hours and I feel that I need to change that because every time I use it I will suffer (mentally).

Because the open source world emphasizes how the decoupling is important, it’s really easy to get my favourite component from one framework and put it into another. If I connect the framework with external module in a plugin – bridge, this should be easy to use for others. And I hope I did this.

10 Comments

  1. Great idea to bridge the two components. I’m confused by your example route though. What is it actually supposed to do? Why is slug defined if it’s a routing token?

    If I assumed that it was to view a blog post I would create a route like:
    `Router::connect(‘/blog/:slug’, ['controller' => 'blogs', 'action' => 'post'], ['slug' => '[a-z0-9-]+’, ‘pass’ => ['slug']);`

    Then in your `BlogsController::post($slug)` method you would get the slug as a passed parameter. To create a link to view this post I would create a link as `echo $this->Html->link(‘View post’, ['controller' => 'blogs', 'action' => 'post', 'slug' => $blog['Post']['id']]);`

    Reply
  2. David,
    your example is good. I cannot fight with that. My point was not to pass all parameters (as in your example) to every single link – ‘echo $this->Html->link(‘View post’, ['controller' => 'blogs', 'action' => 'post', 'slug' => $blog['Post']['id']]);’ should be called without controller, action and template. I want to pass there really, really mandatory “slug”.

    So if the CakePHP has an option to create an url and call it by echo `$this->Html->link(‘View post’, ‘blog_post’, ['slug'=>$blog['Post']['slug']]);` then I’m ok.

    Piotr

    Reply

Leave a Comment.