New in Symfony 4.1: Inlined routing configuration

Contributed by
Nicolas Grekas
in #26518.

The Symfony Routing component allows to define requirements and default values for route placeholders using the requirements and defaults options respectively.

For example, in the following route defined with PHP annotations, the page placeholder is restricted to only accept integers and its default value is 1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use Symfony\Component\Routing\Annotation\Route;

class BlogController extends Controller
{
    /**
     * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}, defaults={"page"="1"})
     */
    public function list($page)
    {
        // ...
    }
}

This route config is a bit verbose for simple conditions. That's why in Symfony 4.1 you can inline the route requirements and default values in the placeholders. The previous example in Symfony 4.1 can look like this:

1
2
3
4
5
6
7
/**
 * @Route("/blog/{page<\d+>?1}", name="blog_list")
 */
public function list($page)
{
    // ...
}

The new syntax is {placeholder-name<requirements>?defaults}, every part of it is optional and it works in all config formats (annotations, YAML and XML):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
blog_list:
    # no requirements and no default value
    path: /blog/{page}
    # with requirements but no default value
    path: /blog/{page<\d+>}
    # no requirements but with a default value
    path: /blog/{page?1}
    # no requirements but with default value = null
    path: /blog/{page?}
    # with requirements and default value = null
    path: /blog/{page<.*>?}

You can inline the config for multiple placeholders in the same route, but if there are lots of placeholders or the conditions are complex, the resulting config may be less readable and you should probably revert to the previous syntax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// this config may be too complex to inline it:

/** @Route("/{_locale<en|es|fr>?en}/blog/{category<news|releases|security>?news}/{page<\d+>?1}", name="blog_list") */
public function list($page) { }

// in this case it may be better to keep using the traditional syntax

/**
 * @Route("/{_locale}/blog/{category}/{page}", name="blog_list",
 *   "requirements"={"_locale": "en|es|fr", "category": "news|releases|security", "page": "\d"},
 *   "defaults"={"_locale": "en", "category": "news", "page": "1"}
 * )
 */
public function list($page) { }

Comments

Awesome!
Very nice!
It's really cool!!!
Is it possible to mix?

eg:

```php
/**
* @Route("/{_locale?en}/blog/{category}/{page?1}", name="blog_list",
* "requirements"={"_locale": "en|es|fr", "page": "\d"},
* "defaults"={"category": "news"}
* )
*/
public function list($page) { }
```
@Vinorcola yes, you can mix both configs in the same route!
Nice feature, but I think this feature adds some difficulties in reading routes, especially when it comes to complex routes like in the example! there is a best practice to when to use the new method or the old one?
@zairig I agree with you. This new syntax doesn't make sense in complex routes (at least to me!) as explained in the article. There's no formal recommendation about when to use it or not use it. I'd say each developer will need to trust their instincts and don't use it when you feel that the new config is too unreadable.
I’m really liking the look of inlined defaults and uninlined requirements for the multi-placeholder routes in my projects.
Quite useful feature. Nice work(y)
Perfect !
It's great! Thanks!
Solid improvement to routing! Great work everybody!
Login with SensioLabsConnect to post a comment