Scaling Silex applications (part II). Using RouteCollection


In the post Scaling Silex applications I wanted to organize a one Silex application. In one comment Igor Wiedler recommended us to use RouteCollections instead of define the routes with a Symfony’s Dependency Injection Container. Because of that I started to hack a little bit about it and here I show you my outcomes:

I want to build an imaginary application with silex. This application has also one Api and one little blog. I want to organize those parts. Our index.php file

<?php
// www/index.php
require_once __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;

$app = new Application();

$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
    $loader     = new YamlFileLoader(new FileLocator(__DIR__ . '/../config'));
    $collection = $loader->load('routes.yml');
    $routes->addCollection($collection);

    return $routes;
});

$app->run();

Now our routes.yml file:

# config/routes.yml

home:
  path: /
  defaults: { _controller: 'Gonzalo123\AppController::homeAction' }

hello:
  path: /hello/{name}
  defaults: { _controller: 'Gonzalo123\AppController::helloAction' }

api:
  prefix: /api
  resource: api.yml

blog:
  prefix: /blog
  resource: blog.yml

As we can see we have separated the main routing file into different files: api.yml (for the Api) and blog.yml (for the blog)

# config/api.yml

api.list:
  path:     /list
  defaults: { _controller: 'Gonzalo123\ApiController::listAction' }
# blog.yml

blog.home:
  path:     /
  defaults: { _controller: 'Gonzalo123\BlogController::homeAction' }

And now we can create our controllers:

<?php
// lib/Gonzalo123/AppController.php

namespace Gonzalo123;

use Symfony\Component\HttpFoundation\Response;
use Silex\Application;

class AppController
{
    public function homeAction()
    {
        return new Response("AppController::homeAction");
    }

    public function helloAction(Application $app, $name)
    {
        return new Response("Hello" . $app->escape($name));
    }
}
<?php
// lib/Gonzalo123/ApiController.php

namespace Gonzalo123;

use Symfony\Component\HttpFoundation\Response;

class ApiController
{
    public function listAction()
    {
        return new Response("AppController::listAction");
    }
}
<?php
// lib/Gonzalo123/BlogController.php

namespace Gonzalo123;

use Symfony\Component\HttpFoundation\Response;

class BlogController
{
    public function homeAction()
    {
        return new Response("BlogController::homeAction");
    }
}

And that’s all. Here also the needed dependencies within our composer.json file

{
    "require":{
        "silex/silex":"1.0.*@dev",
        "symfony/yaml":"v2.2.0",
        "symfony/config":"v2.2.0"
    },
    "autoload":{
        "psr-0":{
            "":"lib/"
        }
    }
}

source code at github.

27 thoughts on “Scaling Silex applications (part II). Using RouteCollection

  1. i try to add weprofierProvider like this :
    // web profiler service
    $app->register($profiler = new \Silex\Provider\WebProfilerServiceProvider(), array(
    ‘profiler.cache_dir’ => __DIR__.’/../cache’,
    ));
    $app->mount(‘/_profiler’, $profiler);

    but this make an error:
    Twig_Error_Syntax: The function “path” does not exist in “@WebProfiler/Profiler/toolbar_js.html.twig” at line 15

    1. I don’t understand your problem. It looks that it’s out of the scope of this post. To use the WebProfilerServiceProvider you need to follow the instructions that appear within the WebProfilerServiceProvider documentation. It works without problems.

  2. Good solution. Thanks.
    But how can i set and use custom vars (ex. layouts) for any routes/collections in yaml file?
    And what about middlewares?

    1. AFAIK we can use all the functionality defined within the routing component fo sf2. http://symfony.com/doc/2.0/book/routing.html. I must check out how to handle middlewares. The approach of the firsr article writes a silex app from a YAML file. That’s cool because we can do all the things that Silex does, But it’s bad because we need to code them. This second approach uses one existing component. It looks smarter, but I need to invetigate your question about middlewares

  3. Good Job.

    I’m new in Silex. (Since Bilbostack speach of Eguiluz 🙂 Old school PHP programmer, with very bad habits, trying to re-learn. And bad english :$

    One question. ¿how can you access $app and $request from homeAction() ? Don’t want to do with global, neither passing in all the methods… :$

    (no se si se me entiende… 😀 )

    1. be careful. If you use globals you will burn in hell :). It’spretty straightforward. You can use Request through Dependency injection and if you need $app you need to use use keyword:

      example:


      use Symfony\Component\HttpFoundation\Request;

      ...

      $app->get('/', function($Request $request) use ($app){
      ...
      });

      (y sí. Sí que se te entiende 🙂 )

    1. AFAIK you cannot use middlewares when using route collection. In the previous post we discus about it in the comments, but when we are using route collection we cannot define middlewares (before and after) in the yaml file (of course if we hack a little bit). I think the best solution is to use the event dispatcher.

    1. You don’t need to inject it. RouteCollection does it. As you can see in the examples, you can use parameters in public functions and Silex call them with with the properly request parameter. But also you can inject the whole SilexApplication. You only need to define the type of the input parameter with the properly type (Silex\Application). This is called “Hollywood principle”

  4. Hi! First of all, good job and thanks for share your knowlodge with us! I guess there is a little mistake in your code. In ApiController.php you put the code ‘return new Response(“AppController::listAction”);’. I guess you should write ‘return new Response(“ApiController::listAction”);’. Am I wrong?

    Bye

  5. I Using version 1.2 Silex

    {
    “require”: {
    “silex/silex”: “~1.2”,
    “symfony/yaml”:”v2.2.0″,
    “symfony/config”:”v2.2.0″
    },
    “autoload”:{
    “psr-0”:{
    “”:”src/”
    }
    }

    }

    Not working others routers,Only this router…

    home:
    path: /
    defaults: { _controller: ‘Gonzalo123\AppController::homeAction’ }

    Where is the wrong?

  6. Hi Gonzalo,

    Sorry, I know this post is a little bit old, but … do you know if it’s common (or possible) in Silex/Symfony set a route where I can receive from the url the name of the action to be called (ex: /foo/{action}/)? I have a lot of actions in my app and a I think it will be more
    lighter than set all controller::action routes individually.

    1. I’m not sure. The first thing come to my mind is create a proxy action and in this action parse uri and redirect to action depending on the request.

  7. Im trying your example. But get an error when trying the following:

    use Path\To\MyApplication;

    $application = new MyApplication();

    $application[‘routes’] = $application->extend(‘routes’,function (RouteCollection $routes,Application $application){
    });

    Error:
    Argument 2 passed to {closure}() must be an instance of Application

    I have a external MyApplication.php that´s holds the following:

    namespace Path\To;

    use Silex\Application;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Config\FileLocator;
    use symfony\Component\Routing\Loader\YamlFileLoader;
    use symfony\Component\Routing\RouteCollection;
    use Silex\Provider;

    class MyApplication extends \Silex\Application
    {

    public function myLoader(){

    }

    }

    Is my Pimple to old. I´m using psr-4 and I can see in the Pimple lib composer.json file that it´s using psr-0

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.