I have a route with 2 parameters:
BBBundle_blog_show:
pattern: /{id}/{slug}
defaults: { _controller: BloggerBlogBundle:Blog:show }
requirements:
A while ago, I decided I was annoyed by being unable to pass objects as route parameters. I had to concern myself with knowledge of routes and the exact parameter values within templates and other things generating those routes.
I've build this bundle for symfony, which allows you to use and extend this ability (Symfony 2.7 and higher). Please take a look: https://github.com/iltar/http-bundle. It's also available on Packagist as iltar/http-bundle.
The best thing about this bundle is that you don't need to use another router object or generator. It's just turning on the bundle, adjusting the config to your needs if the defaults don't work out for your preferences and you're good to go. The readme should explain everything you need to know but here's a snippet:
Old style:
/**
* @Route("/profile/{user}/", name="app.view-profile")
*/
public function viewProfileAction(AppUser $user);
// php
$router->generate('app.view-profile', ['user' => $user->getId()]);
// twig
{{ path('app.view-profile', { 'user': user.id }) }}
{{ path('app.view-profile', { 'user': user.getid }) }}
{{ path('app.view-profile', { 'user': user.getId() }) }}
{{ path('app.view-profile', { 'user': user[id] }) }}
New style:
// php
$router->generate('app.view-profile', ['user' => $user]);
// twig
{{ path('app.view-profile', { 'user' : user }) }}
This is what I came up with eventually:
I implemented by own generator base class that looks for 'object' parameter and tries to get required parameters from that object.
//src/Blogger/BlogBundle/Resources/config/services.yml parameters: router.options.generator_base_class: Blogger\BlogBundle\Routing\Generator\UrlGenerator
//src/Blogger/BlogBundle/Routing/Generator/UrlGenerator.php
namespace Blogger\BlogBundle\Routing\Generator;
use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
use Doctrine\Common\Util\Inflector;
/**
* UrlGenerator generates URL based on a set of routes.
*
* @api
*/
class UrlGenerator extends BaseUrlGenerator
{
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
{
if (isset($parameters['object']) && is_object($parameters['object'])) {
$object = $parameters['object'];
$parameters = array_replace($this->context->getParameters(), $parameters);
$tparams = array_replace($defaults, $parameters);
$requiredParameters = array_diff_key(array_flip($variables), $tparams);
$parameters = $this->getParametersFromObject(array_flip($requiredParameters), $object);
}
return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute);
}
protected function getParametersFromObject($keys, $object)
{
$parameters = array();
foreach ($keys as $key) {
$method = 'get' . Inflector::classify($key);
if (method_exists($object, $method)) {
$parameters[$key] = $object->$method();
}
}
return $parameters;
}
}
Now I can write: {{ path('BBBundle_blog_show', {'object': blog}) }} and it will get required parameters (id, slug) from object.