Vladimir Luchaninov
Contributed by Vladimir Luchaninov in #29599

In Symfony applications, you can give route placeholders a default value so they can be omitted when generating the URL. Moreover, if the placeholder is at the end of the route path, it will be removed entirely from the generated URL. Consider this route definition where the page route placeholder is at the end of the route path and provides a default value via the method argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class BlogController extends AbstractController
{
    /**
     * @Route("/blog/{page}", name="blog_list")
     */
    public function list($page = 1)
    {
        // ...
    }
}

If you don't provide the value of the page variable when generating the URL for the blog_list route, the resulting URL will be /blog and the value of the page placeholder will be 1:

1
2
3
4
5
$router = ... // a UrlGeneratorInterface instance

$url = $router->generate('blog_list');                // $url = '/blog'
$url = $router->generate('blog_list', ['page' => 1]); // $url = '/blog/1'
$url = $router->generate('blog_list', ['page' => 7]); // $url = '/blog/7'

Although this is the desired behavior in most applications, sometimes you may prefer to always include the value of the placeholder, even when you don't provide it while generating the URL. In Symfony 4.3 we made this possible with a new syntax for route placeholders:

1
2
3
4
5
6
7
/**
 * @Route("/blog/{!page}", name="blog_list")
 */
public function list($page = 1)
{
    // ...
}

The ! character before the placeholder name tells Symfony to always include its value in the generated URL, no matter if it's a default value:

1
2
3
$url = $router->generate('blog_list');                // $url = '/blog/1'
$url = $router->generate('blog_list', ['page' => 1]); // $url = '/blog/1'
$url = $router->generate('blog_list', ['page' => 7]); // $url = '/blog/7'

This new feature is also useful to always display the value of the special _format routing parameter. This allows for example to display the html extension for all pages in a web application that supports multiple formats:

1
2
3
4
5
6
7
/**
 * @Route("/blog/{page}.{!_format<html|json>?html}", name="blog_list")
 */
public function list($page = 1)
{
    // ...
}
Published in #Living on the edge