Silex, mount multiple controller providers with closures
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.
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.