Question

I'm trying out Silex and I'm having a bit of a problem, or I might say, more of an inconvenience...

I'm trying to load 2 routes from 2 separate yaml files, but for some reason the mounting ($app->mount(...)) doesn't work with closures.

Here's some code:

// load configuration
$loader->load('core.yml');
$loader->load('api.yml');


function bla($app, $container, $key) {
    $myApp = $app['controllers_factory'];

    foreach ($container->getExtensionConfig('routes')[$key] as $name => $route) {
        $controller = $myApp->match($route['pattern'], $route['controller']);
        $controller->method($route['requirements']['_method']);
        $controller->bind($name);
    }
    return $myApp;
}

$app->mount('/core', bla($app, $container, 0));
$app->mount('/api', bla($app, $container, 1));

This works.

What doesn't work is if I do the exact same thing with closures, like this:

$app->mount('/core', function ($app, $container, $key) {
    return $app['controllers_factory'];
});

Gives the following error:

LogicException: The "mount" method takes either a ControllerCollection or a ControllerProviderInterface instance.

But

var_dump($app['controllers_factory']);

spits out an object of type Silex\ControllerCollection.

I'm obviously missing something.

Thank you for your help.

Was it helpful?

Solution

The problem

In your first example, you're mounting the result of a function. In your second example, you're mounting the function itself.

Function bla() returns the controller collection when it's called. When you do

$app->mount('/core', bla($app, $container, 0));

the function is executed first and then the returned ControllerCollection is mounted.

But when you do

$app->mount('/core', function ($app, $container, $key) {...});

the function is not executed. It is treated as an object and mounted. Since the function itstelf is not a ControllerCollection or a ControllerProviderInterface, you get the error.

Two alternatives

Use PHP routing

This is how I like to do it. I don't know if this is "the Silex way", but it works well for me.

You mount each controller collection like so:

$app->mount('/core', include 'controllers/core.php');
$app->mount('/api', include 'controllers/api.php');

Each controller collection goes in a separate file in the controllers folder. So api.php might look like this:

$controllers = $app['controllers_factory'];

$controllers->get('/version', function() use ($app) {
  // do whatever you want
  return 'version 1.2';
});

return $controllers;

There may even be a way of doing this using the YML loader and keeping your routes in yml files, but I don't like mixing yml and php in general. Why use two technologies when you can just use one.

A fancier way

Take a look at this article. His technique is way more elegant than mine, but also more complicated. It's probably better for larger projects. Maybe it will work better for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top