Routing

When your application receives a request, it executes acontroller action to generate the response. The routingconfiguration defines which action to run for each incoming URL. It alsoprovides other useful features, like generating SEO-friendly URLs (e.g./read/intro-to-symfony instead of index.php?article_id=57).

Creating Routes

Routes can be configured in YAML, XML, PHP or using annotations. All formatsprovide the same features and performance, so choose your favorite.Symfony recommends annotationsbecause it's convenient to put the route and controller in the same place.

Creating Routes as Annotations

Run this command once in your application to add support for annotations:

  1. $ composer require annotations

In addition to installing the needed dependencies, this command creates thefollowing configuration file:

  1. # config/routes.yaml
  2. controllers:
  3. resource: '../src/Controller/'
  4. type: annotation

This configuration tells Symfony to look for routes defined as annotations inany PHP class stored in the src/Controller/ directory.

Suppose you want to define a route for the /blog URL in your application. Todo so, create a controller class like the following:

  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. /**
  10. * @Route("/blog", name="blog_list")
  11. */
  12. public function list()
  13. {
  14. // ...
  15. }
  16. }

This configuration defines a route called blog_list that matches when theuser requests the /blog URL. When the match occurs, the application runsthe list() method of the BlogController class.

Note

The query string of a URL is not considered when matching routes. In thisexample, URLs like /blog?foo=bar and /blog?foo=bar&bar=foo willalso match the blog_list route.

The route name (blog_list) is not important for now, but it will beessential later when generating URLs. You onlyhave to keep in mind that each route name must be unique in the application.

Creating Routes in YAML, XML or PHP Files

Instead of defining routes in the controller classes, you can define them in aseparate YAML, XML or PHP file. The main advantage is that they don't requireany extra dependency. The main drawback is that you have to work with multiplefiles when checking the routing of some controller action.

The following example shows how to define in YAML/XML/PHP a route calledblog_list that associates the /blog URL with the list() action ofthe BlogController:

  • YAML
  1. # config/routes.yaml
  2. blog_list:
  3. path: /blog
  4. # the controller value has the format 'controller_class::method_name'
  5. controller: App\Controller\BlogController::list
  6.  
  7. # if the action is implemented as the __invoke() method of the
  8. # controller class, you can skip the '::method_name' part:
  9. # controller: App\Controller\BlogController
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <!-- the controller value has the format 'controller_class::method_name' -->
  9. <route id="blog_list" path="/blog"
  10. controller="App\Controller\BlogController::list"/>
  11.  
  12. <!-- if the action is implemented as the __invoke() method of the
  13. controller class, you can skip the '::method_name' part:
  14. controller="App\Controller\BlogController"/> -->
  15. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_list', '/blog')
  7. // the controller value has the format [controller_class, method_name]
  8. ->controller([BlogController::class, 'list'])
  9.  
  10. // if the action is implemented as the __invoke() method of the
  11. // controller class, you can skip the ', method_name]' part:
  12. // ->controller([BlogController::class])
  13. ;
  14. };

Matching HTTP Methods

By default, routes match any HTTP verb (GET, POST, PUT, etc.)Use the methods option to restrict the verbs each route should respond to:

  • Annotations
  1. // src/Controller/BlogApiController.php
  2. namespace App\Controller;
  3.  
  4. // ...
  5.  
  6. class BlogApiController extends AbstractController
  7. {
  8. /**
  9. * @Route("/api/posts/{id}", methods={"GET","HEAD"})
  10. */
  11. public function show(int $id)
  12. {
  13. // ... return a JSON response with the post
  14. }
  15.  
  16. /**
  17. * @Route("/api/posts/{id}", methods={"PUT"})
  18. */
  19. public function edit(int $id)
  20. {
  21. // ... edit a post
  22. }
  23. }
  • YAML
  1. # config/routes.yaml
  2. api_post_show:
  3. path: /api/posts/{id}
  4. controller: App\Controller\BlogApiController::show
  5. methods: GET|HEAD
  6.  
  7. api_post_edit:
  8. path: /api/posts/{id}
  9. controller: App\Controller\BlogApiController::edit
  10. methods: PUT
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="api_post_show" path="/api/posts/{id}"
  9. controller="App\Controller\BlogApiController::show"
  10. methods="GET|HEAD"/>
  11.  
  12. <route id="api_post_edit" path="/api/posts/{id}"
  13. controller="App\Controller\BlogApiController::edit"
  14. methods="PUT"/>
  15. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogApiController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('api_post_show', '/api/posts/{id}')
  7. ->controller([BlogApiController::class, 'show'])
  8. ->methods(['GET', 'HEAD'])
  9. ;
  10. $routes->add('api_post_edit', '/api/posts/{id}')
  11. ->controller([BlogApiController::class, 'edit'])
  12. ->methods(['PUT'])
  13. ;
  14. };

Tip

HTML forms only support GET and POST methods. If you're calling aroute with a different method from an HTML form, add a hidden field calledmethod with the method to use (e.g. <input type="hidden" name="_method" value="PUT"/>).If you create your forms with [_Symfony Forms]($6b687f866aac8d6e.md) this is doneautomatically for you.

Matching Expressions

Use the condition option if you need some route to match based on somearbitrary matching logic:

  • Annotations
  1. // src/Controller/DefaultController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class DefaultController extends AbstractController
  8. {
  9. /**
  10. * @Route(
  11. * "/contact",
  12. * name="contact",
  13. * condition="context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'"
  14. * )
  15. *
  16. * expressions can also include config parameters:
  17. * condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
  18. */
  19. public function contact()
  20. {
  21. // ...
  22. }
  23. }
  • YAML
  1. # config/routes.yaml
  2. contact:
  3. path: /contact
  4. controller: 'App\Controller\DefaultController::contact'
  5. condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'"
  6. # expressions can also include config parameters:
  7. # condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="contact" path="/contact" controller="App\Controller\DefaultController::contact">
  9. <condition>context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'</condition>
  10. <!-- expressions can also include config parameters: -->
  11. <!-- <condition>request.headers.get('User-Agent') matches '%app.allowed_browsers%'</condition> -->
  12. </route>
  13. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\DefaultController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('contact', '')
  7. ->controller([DefaultController::class, 'contact'])
  8. ->condition('context.getMethod() in ["GET", "HEAD"] and request.headers.get("User-Agent") matches "/firefox/i"')
  9. // expressions can also include config parameters:
  10. // 'request.headers.get("User-Agent") matches "%app.allowed_browsers%"'
  11. ;
  12. };

The value of the condition option is any validExpressionLanguage expressionand can use any of these variables created by Symfony:

  • context
  • An instance of RequestContext,which holds the most fundamental information about the route being matched.
  • request
  • The Symfony Request object thatrepresents the current request.Behind the scenes, expressions are compiled down to raw PHP. Because of this,using the condition key causes no extra overhead beyond the time it takesfor the underlying PHP to execute.

Caution

Conditions are not taken into account when generating URLs (which isexplained later in this article).

Debugging Routes

As your application grows, you'll eventually have a lot of routes. Symfonyincludes some commands to help you debug routing issues. First, the debug:routercommand lists all your application routes in the same order in which Symfonyevaluates them:

  1. $ php bin/console debug:router
  2.  
  3. ---------------- ------- ------- ----- --------------------------------------------
  4. Name Method Scheme Host Path
  5. ---------------- ------- ------- ----- --------------------------------------------
  6. homepage ANY ANY ANY /
  7. contact GET ANY ANY /contact
  8. contact_process POST ANY ANY /contact
  9. article_show ANY ANY ANY /articles/{_locale}/{year}/{title}.{_format}
  10. blog ANY ANY ANY /blog/{page}
  11. blog_show ANY ANY ANY /blog/{slug}
  12. ---------------- ------- ------- ----- --------------------------------------------

Pass the name (or part of the name) of some route to this argument to print theroute details:

  1. $ php bin/console debug:router app_lucky_number
  2.  
  3. +-------------+---------------------------------------------------------+
  4. | Property | Value |
  5. +-------------+---------------------------------------------------------+
  6. | Route Name | app_lucky_number |
  7. | Path | /lucky/number/{max} |
  8. | ... | ... |
  9. | Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
  10. | | utf8: true |
  11. +-------------+---------------------------------------------------------+

The other command is called router:match and it shows which route will matchthe given URL. It's useful to find out why some URL is not executing thecontroller action that you expect:

  1. $ php bin/console router:match /lucky/number/8
  2.  
  3. [OK] Route "app_lucky_number" matches

Route Parameters

The previous examples defined routes where the URL never changes (e.g. /blog).However, it's common to define routes where some parts are variable. For example,the URL to display some blog post will probably include the title or slug(e.g. /blog/my-first-post or /blog/all-about-symfony).

In Symfony routes, variable parts are wrapped in { … } and they must havea unique name. For example, the route to display the blog post contents isdefined as /blog/{slug}:

  • Annotations
  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. // ...
  10.  
  11. /**
  12. * @Route("/blog/{slug}", name="blog_show")
  13. */
  14. public function show(string $slug)
  15. {
  16. // $slug will equal the dynamic part of the URL
  17. // e.g. at /blog/yay-routing, then $slug='yay-routing'
  18.  
  19. // ...
  20. }
  21. }
  • YAML
  1. # config/routes.yaml
  2. blog_show:
  3. path: /blog/{slug}
  4. controller: App\Controller\BlogController::show
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_show" path="/blog/{slug}"
  9. controller="App\Controller\BlogController::show"/>
  10. </route>
  11. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_show', '/blog/{slug}')
  7. ->controller([BlogController::class, 'show'])
  8. ;
  9. };

The name of the variable part ({slug} in this example) is used to create aPHP variable where that route content is stored and passed to the controller.If a user visits the /blog/my-first-post URL, Symfony executes the show()method in the BlogController class and passes a $slug = 'my-first-post'argument to the show() method.

Routes can define any number of parameters, but each of them can only be usedonce on each route (e.g. /blog/posts-about-{category}/page/{pageNumber}).

Parameters Validation

Imagine that your application has a blog_show route (URL: /blog/{slug})and a blog_list route (URL: /blog/{page}). Given that route parametersaccept any value, there's no way to differentiate both routes.

If the user requests /blog/my-first-post, both routes will match and Symfonywill use the route which was defined first. To fix this, add some validation tothe {page} parameter using the requirements option:

  • Annotations
  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. /**
  10. * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"})
  11. */
  12. public function list(int $page)
  13. {
  14. // ...
  15. }
  16.  
  17. /**
  18. * @Route("/blog/{slug}", name="blog_show")
  19. */
  20. public function show($slug)
  21. {
  22. // ...
  23. }
  24. }
  • YAML
  1. # config/routes.yaml
  2. blog_list:
  3. path: /blog/{page}
  4. controller: App\Controller\BlogController::list
  5. requirements:
  6. page: '\d+'
  7.  
  8. blog_show:
  9. path: /blog/{slug}
  10. controller: App\Controller\BlogController::show
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_list" path="/blog/{page}" controller="App\Controller\BlogController::list">
  9. <requirement key="page">\d+</requirement>
  10. </route>
  11.  
  12. <route id="blog_show" path="/blog/{slug}"
  13. controller="App\Controller\BlogController::show"/>
  14. </route>
  15. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_list', '/blog/{page}')
  7. ->controller([BlogController::class, 'list'])
  8. ->requirements(['page' => '\d+'])
  9. ;
  10.  
  11. $routes->add('blog_show', '/blog/{slug}')
  12. ->controller([BlogController::class, 'show'])
  13. ;
  14. // ...
  15. };

The requirements option defines the PHP regular expressions that routeparameters must match for the entire route to match. In this example, \d+ isa regular expression that matches a digit of any length. Now:

URLRouteParameters
/blog/2blog_list$page = 2
/blog/my-first-postblog_show$slug = my-first-post

Tip

Route requirements (and route paths too) can includecontainer parameters, which is useful todefine complex regular expressions once and reuse them in multiple routes.

Tip

Parameters also support PCRE Unicode properties, which are escapesequences that match generic character types. For example, \p{Lu}matches any uppercase character in any language, \p{Greek} matches anyGreek character, etc.

Note

When using regular expressions in route parameters, you can set the utf8route option to true to make any . character match any UTF-8characters instead of just a single byte.

If you prefer, requirements can be inlined in each parameter using the syntax{parameter_name<requirements>}. This feature makes configuration moreconcise, but it can decrease route readability when requirements are complex:

  • Annotations
  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. /**
  10. * @Route("/blog/{page<\d+>}", name="blog_list")
  11. */
  12. public function list(int $page)
  13. {
  14. // ...
  15. }
  16. }
  • YAML
  1. # config/routes.yaml
  2. blog_list:
  3. path: /blog/{page<\d+>}
  4. controller: App\Controller\BlogController::list
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_list" path="/blog/{page<\d+>}"
  9. controller="App\Controller\BlogController::list"/>
  10.  
  11. <!-- ... -->
  12. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_list', '/blog/{page<\d+>}')
  7. ->controller([BlogController::class, 'list'])
  8. ;
  9. // ...
  10. };

Optional Parameters

In the previous example, the URL of blog_list is /blog/{page}. If usersvisit /blog/1, it will match. But if they visit /blog, it will notmatch. As soon as you add a parameter to a route, it must have a value.

You can make blog_list once again match when the user visits /blog byadding a default value for the {page} parameter. When using annotations,default values are defined in the arguments of the controller action. In theother configuration formats they are defined with the defaults option:

  • Annotations
  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. /**
  10. * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"})
  11. */
  12. public function list(int $page = 1)
  13. {
  14. // ...
  15. }
  16. }
  • YAML
  1. # config/routes.yaml
  2. blog_list:
  3. path: /blog/{page}
  4. controller: App\Controller\BlogController::list
  5. defaults:
  6. page: 1
  7. requirements:
  8. page: '\d+'
  9.  
  10. blog_show:
  11. # ...
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_list" path="/blog/{page}" controller="App\Controller\BlogController::list">
  9. <default key="page">1</default>
  10.  
  11. <requirement key="page">\d+</requirement>
  12. </route>
  13.  
  14. <!-- ... -->
  15. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_list', '/blog/{page}')
  7. ->controller([BlogController::class, 'list'])
  8. ->defaults(['page' => 1])
  9. ->requirements(['page' => '\d+'])
  10. ;
  11. };

Now, when the user visits /blog, the blog_list route will match and$page will default to a value of 1.

Caution

You can have more than one optional parameter (e.g. /blog/{slug}/{page}),but everything after an optional parameter must be optional. For example,/{page}/blog is a valid path, but page will always be required(i.e. /blog will not match this route).

Note

Routes with optional parameters at the end will not match on requestswith a trailing slash (i.e. /blog/ will not match, /blog will match).

If you want to always include some default value in the generated URL (forexample to force the generation of /blog/1 instead of /blog in theprevious example) add the ! character before the parameter name: /blog/{!page}

New in version 4.3: The feature to force the inclusion of default values in generated URLs wasintroduced in Symfony 4.3.

As it happens with requirements, default values can also be inlined in eachparameter using the syntax {parameter_name?default_value}. This featureis compatible with inlined requirements, so you can inline both in a singleparameter:

  • Annotations
  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Routing\Annotation\Route;
  6.  
  7. class BlogController extends AbstractController
  8. {
  9. /**
  10. * @Route("/blog/{page<\d+>?1}", name="blog_list")
  11. */
  12. public function list(int $page)
  13. {
  14. // ...
  15. }
  16. }
  • YAML
  1. # config/routes.yaml
  2. blog_list:
  3. path: /blog/{page<\d+>?1}
  4. controller: App\Controller\BlogController::list
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="blog_list" path="/blog/{page <\d+>?1}"
  9. controller="App\Controller\BlogController::list"/>
  10.  
  11. <!-- ... -->
  12. </routes>
  • PHP
  1. // config/routes.php
  2. use App\Controller\BlogController;
  3. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  4.  
  5. return function (RoutingConfigurator $routes) {
  6. $routes->add('blog_list', '/blog/{page<\d+>?1}')
  7. ->controller([BlogController::class, 'list'])
  8. ;
  9. };

Tip

To give a null default value to any parameter, add nothing after the? character (e.g. /blog/{page?}).

Parameter Conversion

A common routing need is to convert the value stored in some parameter (e.g. aninteger acting as the user ID) into another value (e.g. the object thatrepresents the user). This feature is called "param converter" and is onlyavailable when using annotations to define routes.

In case you didn't run this command before, run it now to add support forannotations and "param converters":

  1. $ composer require annotations

Now, keep the previous route configuration, but change the arguments of thecontroller action. Instead of string $slug, add BlogPost $post:

  1. // src/Controller/BlogController.php
  2. namespace App\Controller;
  3.  
  4. use App\Entity\BlogPost;
  5. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  6. use Symfony\Component\Routing\Annotation\Route;
  7.  
  8. class BlogController extends AbstractController
  9. {
  10. // ...
  11.  
  12. /**
  13. * @Route("/blog/{slug}", name="blog_show")
  14. */
  15. public function show(BlogPost $post)
  16. {
  17. // $post is the object whose slug matches the routing parameter
  18.  
  19. // ...
  20. }
  21. }

If your controller arguments include type-hints for objects (BlogPost inthis case), the "param converter" makes a database request to find the objectusing the request parameters (slug in this case). If no object is found,Symfony generates a 404 response automatically.

Read the full param converter documentation to learn about the convertersprovided by Symfony and how to configure them.

Special Parameters

In addition to your own parameters, routes can include any of the followingspecial parameters created by Symfony:

  • _controller
  • This parameter is used to determine which controller and action is executedwhen the route is matched.
  • _format
  • The matched value is used to set the "request format" of the Request object.This is used for such things as setting the Content-Type of the response(e.g. a json format translates into a Content-Type of application/json).
  • _fragment
  • Used to set the fragment identifier, which is the optional last part of a URL thatstarts with a # character and is used to identify a portion of a document.
  • _locale
  • Used to set the locale on the request.You can include these attributes (except _fragment) both in individual routesand in route imports. Symfony defines some special attributes with the same name(except for the leading underscore) so you can define them easier:
  • Annotations
  1. // src/Controller/ArticleController.php
  2.  
  3. // ...
  4. class ArticleController extends AbstractController
  5. {
  6. /**
  7. * @Route(
  8. * "/articles/{_locale}/search.{_format}",
  9. * locale="en",
  10. * format="html",
  11. * requirements={
  12. * "_locale": "en|fr",
  13. * "_format": "html|xml",
  14. * }
  15. * )
  16. */
  17. public function search()
  18. {
  19. }
  20. }
  • YAML
  1. # config/routes.yaml
  2. article_search:
  3. path: /articles/{_locale}/search.{_format}
  4. controller: App\Controller\ArticleController::search
  5. locale: en
  6. format: html
  7. requirements:
  8. _locale: en|fr
  9. _format: html|xml
  • XML
  1. <!-- config/routes.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <routes xmlns="http://symfony.com/schema/routing"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/routing
  6. https://symfony.com/schema/routing/routing-1.0.xsd">
  7.  
  8. <route id="article_search"
  9. path="/articles/{_locale}/search.{_format}"
  10. controller="App\Controller\ArticleController::search"
  11. locale="en"
  12. format="html">
  13.  
  14. <requirement key="_locale">en|fr</requirement>
  15. <requirement key="_format">html|rss</requirement>
  16.  
  17. </route>
  18. </routes>
  • PHP
  1. // config/routes.php
  2. namespace Symfony\Component\Routing\Loader\Configurator;
  3.  
  4. use App\Controller\ArticleController;
  5.  
  6. return function (RoutingConfigurator $routes) {
  7. $routes->add('article_show', '/articles/{_locale}/search.{_format}')
  8. ->controller([ArticleController::class, 'search'])
  9. ->locale('en')
  10. ->format('html')
  11. ->requirements([
  12. '_locale' => 'en|fr',
  13. '_format' => 'html|rss',
  14. ])
  15. ;
  16. };

New in version 4.3: The special attributes were introduced in Symfony 4.3.

Extra Parameters

In the defaults option of a route you can optionally define parameters notincluded in the route configuration. This is useful to pass extra arguments tothe controllers of the routes:

  • Annotations
use Symfony\Component\Routing\Annotation\Route;

class BlogController
{
    /**
     * @Route("/blog/{page}", name="blog_index", defaults={"page": 1, "title": "Hello world!"})
     */
    public function index(int $page, string $title)
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
blog_index:
    path:       /blog/{page}
    controller: App\Controller\BlogController::index
    defaults:
        page: 1
        title: "Hello world!"
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog_index" path="/blog/{page}" controller="App\Controller\BlogController::index">
        <default key="page">1</default>
        <default key="title">Hello world!</default>
    </route>
</routes>
  • PHP
// config/routes.php
use App\Controller\BlogController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('blog_index', '/blog/{page}')
        ->controller([BlogController::class, 'index'])
        ->defaults([
            'page'  => 1,
            'title' => 'Hello world!',
        ])
    ;
};

Slash Characters in Route Parameters

Route parameters can contain any values except the / slash character,because that's the character used to separate the different parts of the URLs.For example, if the token value in the /share/{token} route contains a/ character, this route won't match.

A possible solution is to change the parameter requirements to be more permissive:

  • Annotations
use Symfony\Component\Routing\Annotation\Route;

class DefaultController
{
    /**
     * @Route("/share/{token}", name="share", requirements={"token"=".+"})
     */
    public function share($token)
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
share:
    path:       /share/{token}
    controller: App\Controller\DefaultController::share
    requirements:
        token: .+
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="share" path="/share/{token}" controller="App\Controller\DefaultController::share">
        <requirement key="token">.+</requirement>
    </route>
</routes>
  • PHP
// config/routes.php
use App\Controller\DefaultController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('share', '/share/{token}')
        ->controller([DefaultController::class, 'share'])
        ->requirements([
            'token' => '.+',
        ])
    ;
};

Note

If the route defines several parameter and you apply this permissiveregular expression to all of them, the results won't be the expected. Forexample, if the route definition is /share/{path}/{token} and bothpath and token accept /, then path will contain its contentsand the token, and token will be empty.

Note

If the route includes the special {_format} parameter, you shouldn'tuse the .+ requirement for the parameters that allow slashes. For example,if the pattern is /share/{token}.{_format} and {token} allows anycharacter, the /share/foo/bar.json URL will consider foo/bar.jsonas the token and the format will be empty. This can be solved by replacingthe .+ requirement by [^.]+ to allow any character except dots.

Route Groups and Prefixes

It's common for a group of routes to share some options (e.g. all routes relatedto the blog start with /blog) That's why Symfony includes a feature to shareroute configuration.

When defining routes as annotations, put the common configuration in the@Route annotation of the controller class. In other routing formats, definethe common configuration using options when importing the routes.

  • Annotations
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route("/blog", requirements={"locale": "en|es|fr"}, name="blog_")
 */
class BlogController
{
    /**
     * @Route("/{_locale}", name="index")
     */
    public function index()
    {
        // ...
    }

    /**
     * @Route("/{_locale}/posts/{slug}", name="post")
     */
    public function show(Post $post)
    {
        // ...
    }
}
  • YAML
# config/routes/annotations.yaml
controllers:
    resource: '../src/Controller/'
    type: annotation
    # this is added to the beginning of all imported route URLs
    prefix: '/blog'
    # this is added to the beginning of all imported route names
    name_prefix: 'blog_'
    # these requirements are added to all imported routes
    requirements:
        locale: 'en|es|fr'
    # An imported route with an empty URL will become "/blog/"
    # Uncomment this option to make that URL "/blog" instead
    # trailing_slash_on_root: false
  • XML
<!-- config/routes/annotations.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <!--
        the 'prefix' value is added to the beginning of all imported route URLs
        the 'name-prefix' value is added to the beginning of all imported route names
    -->
    <import resource="../src/Controller/"
        type="annotation"
        prefix="/blog"
        name-prefix="blog_">
        <!-- these requirements are added to all imported routes -->
        <requirement key="locale">en|es|fr</requirement>
    </import>

    <!-- An imported route with an empty URL will become "/blog/"
         Uncomment this option to make that URL "/blog" instead -->
    <import resource="../src/Controller/" type="annotation"
            prefix="/blog"
            trailing-slash-on-root="false">
            <!-- ... -->
    </import>
</routes>
  • PHP
// config/routes/annotations.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->import('../src/Controller/', 'annotation')
        // this is added to the beginning of all imported route URLs
        ->prefix('/blog')
        // An imported route with an empty URL will become "/blog/"
        // Pass FALSE as the second argument to make that URL "/blog" instead
        // ->prefix('/blog', false)
        // this is added to the beginning of all imported route names
        ->namePrefix('blog_')
        // these requirements are added to all imported routes
        ->requirements(['locale' => 'en|es|fr'])
    ;
};

In this example, the route of the index() action will be called blog_indexand its URL will be /blog/. The route of the show() action will be calledblog_post and its URL will be /blog/{_locale}/posts/{slug}. Both routeswill also validate that the _locale parameter matches the regular expressiondefined in the class annotation.

Symfony can import routes from different sourcesand you can even create your own route loader.

Getting the Route Name and Parameters

The Request object created by Symfony stores all the route configuration(such as the name and parameters) in the "request attributes". You can get thisinformation in a controller via the Request object:

// src/Controller/BlogController.php
namespace App\Controller;

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

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

        $routeName = $request->attributes->get('_route');
        $routeParameters = $request->attributes->get('_route_params');

        // use this to get all the available attributes (not only routing ones):
        $allAttributes = $request->attributes->all();
    }
}

You can get this information in services too injecting the requeststackservice to [_get the Request object in a service](https://symfony.com/doc/current/service_container/request.html).In templates, use the Twig global app variable to getthe request and its attributes:

{% set route_name = app.request.attributes.get('_route') %}
{% set route_parameters = app.request.attributes.get('_route_params') %}

{# use this to get all the available attributes (not only routing ones) #}
{% set all_attributes = app.request.attributes.all %}

Special Routes

Symfony defines some special controllers to render templates and redirect toother routes from the route configuration so you don't have to create acontroller action.

Rendering a Template Directly from a Route

Read the section about rendering a template from a routein the main article about Symfony templates.

Redirecting to URLs and Routes Directly from a Route

Use the RedirectController to redirect to other routes (redirectAction)and URLs (urlRedirectAction):

  • YAML
# config/routes.yaml
doc_shortcut:
    path: /doc
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction
    defaults:
        route: 'doc_page'
        # optionally you can define some arguments passed to the route
        page: 'index'
        version: 'current'
        # redirections are temporary by default (code 302) but you can make them permanent (code 301)
        permanent: true
        # add this to keep the original query string parameters when redirecting
        keepQueryParams: true
        # add this to keep the HTTP method when redirecting. The redirect status changes
        # * for temporary redirects, it uses the 307 status code instead of 302
        # * for permanent redirects, it uses the 308 status code instead of 301
        keepRequestMethod: true

legacy_doc:
    path: /legacy/doc
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
    defaults:
        # this value can be an absolute path or an absolute URL
        path: 'https://legacy.example.com/doc'
        permanent: true
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="doc_shortcut" path="/doc"
           controller="Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction">
        <default key="route">doc_page</default>
        <!-- optionally you can define some arguments passed to the route -->
        <default key="page">index</default>
        <default key="version">current</default>
        <!-- redirections are temporary by default (code 302) but you can make them permanent (code 301)-->
        <default key="permanent">true</default>
        <!-- add this to keep the original query string parameters when redirecting -->
        <default key="keepQueryParams">true</default>
        <!-- add this to keep the HTTP method when redirecting. The redirect status changes:
             * for temporary redirects, it uses the 307 status code instead of 302
             * for permanent redirects, it uses the 308 status code instead of 301 -->
        <default key="keepRequestMethod">true</default>
    </route>

    <route id="legacy_doc" path="/legacy/doc"
           controller="Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction">
        <!-- this value can be an absolute path or an absolute URL -->
        <default key="path">https://legacy.example.com/doc</default>
        <!-- redirections are temporary by default (code 302) but you can make them permanent (code 301)-->
        <default key="permanent">true</default>
    </route>
</routes>
  • PHP
// config/routes.php
use App\Controller\DefaultController;
use Symfony\Bundle\FrameworkBundle\Controller\RedirectController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('doc_shortcut', '/doc')
        ->controller([RedirectController::class, 'redirectAction'])
         ->defaults([
            'route' => 'doc_page',
            // optionally you can define some arguments passed to the template
            'page' => 'index',
            'version' => 'current',
            // redirections are temporary by default (code 302) but you can make them permanent (code 301)
            'permanent' => true,
            // add this to keep the original query string parameters when redirecting
            'keepQueryParams' => true,
            // add this to keep the HTTP method when redirecting. The redirect status changes:
            // * for temporary redirects, it uses the 307 status code instead of 302
            // * for permanent redirects, it uses the 308 status code instead of 301
            'keepRequestMethod' => true,
        ])
    ;

    $routes->add('legacy_doc', '/legacy/doc')
        ->controller([RedirectController::class, 'urlRedirectAction'])
         ->defaults([
            // this value can be an absolute path or an absolute URL
            'path' => 'https://legacy.example.com/doc',
            // redirections are temporary by default (code 302) but you can make them permanent (code 301)
            'permanent' => true,
        ])
    ;
};

Tip

Symfony also provides some utilities toredirect inside controllers

Redirecting URLs with Trailing Slashes

Historically, URLs have followed the UNIX convention of adding trailing slashesfor directories (e.g. https://example.com/foo/) and removing them to referto files (https://example.com/foo). Although serving different contents forboth URLs is OK, nowadays it's common to treat both URLs as the same URL andredirect between them.

Symfony follows this logic to redirect between URLs with and without trailingslashes (but only for GET and HEAD requests):

Route URLIf the requested URL is /fooIf the requested URL is /foo/
/fooIt matches (200 status response)It makes a 301 redirect to /foo
/foo/It makes a 301 redirect to /foo/It matches (200 status response)

Sub-Domain Routing

Routes can configure a host option to require that the HTTP host of theincoming requests matches some specific value. In the following example, bothroutes match the same path (/) but one of them only responds to a specifichost name:

  • Annotations
// src/Controller/MainController.php
namespace App\Controller;

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

class MainController extends AbstractController
{
    /**
     * @Route("/", name="mobile_homepage", host="m.example.com")
     */
    public function mobileHomepage()
    {
        // ...
    }

    /**
     * @Route("/", name="homepage")
     */
    public function homepage()
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
mobile_homepage:
    path:       /
    host:       m.example.com
    controller: App\Controller\MainController::mobileHomepage

homepage:
    path:       /
    controller: App\Controller\MainController::homepage
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="mobile_homepage"
        path="/"
        host="m.example.com"
        controller="App\Controller\MainController::mobileHomepage"/>

    <route id="homepage" path="/" controller="App\Controller\MainController::homepage"/>
</routes>
  • PHP
// config/routes.php
use App\Controller\MainController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('mobile_homepage', '/')
        ->controller([MainController::class, 'mobileHomepage'])
        ->host('m.example.com')
    ;
    $routes->add('homepage', '/')
        ->controller([MainController::class, 'homepage'])
    ;
};

return $routes;

The value of the host option can include parameters (which is useful inmulti-tenant applications) and these parameters can be validated too withrequirements:

  • Annotations
// src/Controller/MainController.php
namespace App\Controller;

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

class MainController extends AbstractController
{
    /**
     * @Route(
     *     "/",
     *     name="mobile_homepage",
     *     host="{subdomain}.example.com",
     *     defaults={"subdomain"="m"},
     *     requirements={"subdomain"="m|mobile"}
     * )
     */
    public function mobileHomepage()
    {
        // ...
    }

    /**
     * @Route("/", name="homepage")
     */
    public function homepage()
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
mobile_homepage:
    path:       /
    host:       "{subdomain}.example.com"
    controller: App\Controller\MainController::mobileHomepage
    defaults:
        subdomain: m
    requirements:
        subdomain: m|mobile

homepage:
    path:       /
    controller: App\Controller\MainController::homepage
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="mobile_homepage"
        path="/"
        host="{subdomain}.example.com"
        controller="App\Controller\MainController::mobileHomepage">
        <default key="subdomain">m</default>
        <requirement key="subdomain">m|mobile</requirement>
    </route>

    <route id="homepage" path="/" controller="App\Controller\MainController::homepage"/>
</routes>
  • PHP
// config/routes.php
use App\Controller\MainController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('mobile_homepage', '/')
        ->controller([MainController::class, 'mobileHomepage'])
        ->host('{subdomain}.example.com')
        ->defaults([
            'subdomain' => 'm',
        ])
        ->requirements([
            'subdomain' => 'm|mobile',
        ])
    ;
    $routes->add('homepage', '/')
        ->controller([MainController::class, 'homepage'])
    ;
};

In the above example, the subdomain parameter defines a default value becauseotherwise you need to include a domain value each time you generate a URL usingthese routes.

Tip

You can also set the host option when importing routesto make all of them require that host name.

Note

When using sub-domain routing, you must set the Host HTTP headers infunctional tests or routes won't match:

$crawler = $client->request(
    'GET',
    '/',
    [],
    [],
    ['HTTP_HOST' => 'm.example.com']
    // or get the value from some container parameter:
    // ['HTTP_HOST' => 'm.' . $client->getContainer()->getParameter('domain')]
);

Localized Routes (i18n)

If your application is translated into multiple languages, each route can definea different URL per each translation locale. Thisavoids the need for duplicating routes, which also reduces the potential bugs:

  • Annotations
// src/Controller/CompanyController.php
namespace App\Controller;

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

class CompanyController extends AbstractController
{
    /**
     * @Route({
     *     "en": "/about-us",
     *     "nl": "/over-ons"
     * }, name="about_us")
     */
    public function about()
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
about_us:
    path:
        en: /about-us
        nl: /over-ons
    controller: App\Controller\CompanyController::about
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="about_us" controller="App\Controller\CompanyController::about">
        <path locale="en">/about-us</path>
        <path locale="nl">/over-ons</path>
    </route>
</routes>
  • PHP
// config/routes.php
use App\Controller\CompanyController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('about_us', [
        'en' => '/about-us',
        'nl' => '/over-ons',
    ])
        ->controller([CompanyController::class, 'about'])
    ;
};

When a localized route is matched, Symfony uses the same locale automaticallyduring the entire request.

Tip

When the application uses full "language + territory" locales (e.g. fr_FR,fr_BE), if the URLs are the same in all related locales, routes can useonly the language part (e.g. fr) to avoid repeating the same URLs.

A common requirement for internationalized applications is to prefix all routeswith a locale. This can be done by defining a different prefix for each locale(and setting an empty prefix for your default locale if you prefer it):

  • YAML
# config/routes/annotations.yaml
controllers:
    resource: '../src/Controller/'
    type: annotation
    prefix:
        en: '' # don't prefix URLs for English, the default locale
        nl: '/nl'
  • XML
<!-- config/routes/annotations.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="../src/Controller/" type="annotation">
        <!-- don't prefix URLs for English, the default locale -->
        <prefix locale="en"></prefix>
        <prefix locale="nl">/nl</prefix>
    </import>
</routes>
  • PHP
// config/routes/annotations.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->import('../src/Controller/', 'annotation')
        ->prefix([
            // don't prefix URLs for English, the default locale
            'en' => '',
            'nl' => '/nl'
        ])
    ;
};

Generating URLs

Routing systems are bidirectional: 1) they associate URLs with controllers (asexplained in the previous sections); 2) they generate URLs for a given route.Generating URLs from routes allows you to not write the <a href="…">values manually in your HTML templates. Also, if the URL of some route changes,you only have to update the route configuration and all links will be updated.

To generate a URL, you need to specify the name of the route (e.g.blog_show) and the values of the parameters defined by the route (e.g.slug = my-blog-post).

For that reason each route has an internal name that must be unique in theapplication. If you don't set the route name explicitly with the nameoption, Symfony generates an automatic name based on the controller and action.

Generating URLs in Controllers

If your controller extends from the AbstractController,use the generateUrl() helper:

// src/Controller/BlogController.php
namespace App\Controller;

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

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

        // generate a URL with no route arguments
        $signUpPage = $this->generateUrl('sign_up');

        // generate a URL with route arguments
        $userProfilePage = $this->generateUrl('user_profile', [
            'username' => $user->getUsername(),
        ]);

        // generated URLs are "absolute paths" by default. Pass a third optional
        // argument to generate different URLs (e.g. an "absolute URL")
        $signUpPage = $this->generateUrl('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);

        // when a route is localized, Symfony uses by default the current request locale
        // pass a different '_locale' value if you want to set the locale explicitly
        $signUpPageInDutch = $this->generateUrl('sign_up', ['_locale' => 'nl']);
    }
}

Note

If you pass to the generateUrl() method some parameters that are notpart of the route definition, they are included in the generated URL as aquery string::

$this->generateUrl('blog', ['page' => 2, 'category' => 'Symfony']);
// the 'blog' route only defines the 'page' parameter; the generated URL is:
// /blog/2?category=Symfony

If your controller does not extend from AbstractController, you'll need tofetch services in your controller andfollow the instructions of the next section.

Generating URLs in Services

Inject the router Symfony service into your own services and use itsgenerate() method. When using service autowiringyou only need to add an argument in the service constructor and type-hint it withthe UrlGeneratorInterface class:

// src/Service/SomeService.php
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class SomeService
{
    private $router;

    public function __construct(UrlGeneratorInterface $router)
    {
        $this->router = $router;
    }

    public function someMethod()
    {
        // ...

        // generate a URL with no route arguments
        $signUpPage = $this->router->generate('sign_up');

        // generate a URL with route arguments
        $userProfilePage = $this->router->generate('user_profile', [
            'username' => $user->getUsername(),
        ]);

        // generated URLs are "absolute paths" by default. Pass a third optional
        // argument to generate different URLs (e.g. an "absolute URL")
        $signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);

        // when a route is localized, Symfony uses by default the current request locale
        // pass a different '_locale' value if you want to set the locale explicitly
        $signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']);
    }
}

Generating URLs in Templates

Read the section about creating links between pagesin the main article about Symfony templates.

Generating URLs in JavaScript

If your JavaScript code is included in a Twig template, you can use thepath() and url() Twig functions to generate the URLs and store them inJavaScript variables. The escape() function is needed to escape anynon-JavaScript-safe values:

<script>
    const route = "{{ path('blog_show', {slug: 'my-blog-post'})|escape('js') }}";
</script>

If you need to generate URLs dynamically or if you are using pure JavaScriptcode, this solution doesn't work. In those cases, consider using theFOSJsRoutingBundle.

Generating URLs in Commands

Generating URLs in commands works the same asgenerating URLs in services. Theonly difference is that commands are not executed in the HTTP context, so theydon't have access to HTTP requests. In practice, this means that if you generateabsolute URLs, you'll get http://localhost/ as the host name instead of yourreal host name.

The solution is to configure the "request context" used by commands when theygenerate URLs. This context can be configured globally for all commands:

  • YAML
# config/services.yaml
parameters:
    router.request_context.host: 'example.org'
    router.request_context.base_url: 'my/path'
    asset.request_context.base_path: '%router.request_context.base_url%'
  • XML
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <parameters>
        <parameter key="router.request_context.host">example.org</parameter>
        <parameter key="router.request_context.base_url">my/path</parameter>
        <parameter key="asset.request_context.base_path">%router.request_context.base_url%</parameter>
    </parameters>

</container>
  • PHP
// config/services.php
$container->setParameter('router.request_context.host', 'example.org');
$container->setParameter('router.request_context.base_url', 'my/path');
$container->setParameter('asset.request_context.base_path', $container->getParameter('router.request_context.base_url'));

This information can be configured per command too:

// src/Command/SomeCommand.php
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
// ...

class SomeCommand extends Command
{
    private $router;

    public function __construct(RouterInterface $router)
    {
        parent::__construct();

        $this->router = $router;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // these values override any global configuration
        $context = $this->router->getContext();
        $context->setHost('example.com');
        $context->setBaseUrl('my/path');

        // generate a URL with no route arguments
        $signUpPage = $this->router->generate('sign_up');

        // generate a URL with route arguments
        $userProfilePage = $this->router->generate('user_profile', [
            'username' => $user->getUsername(),
        ]);

        // generated URLs are "absolute paths" by default. Pass a third optional
        // argument to generate different URLs (e.g. an "absolute URL")
        $signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);

        // when a route is localized, Symfony uses by default the current request locale
        // pass a different '_locale' value if you want to set the locale explicitly
        $signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']);

        // ...
    }
}

Checking if a Route Exists

In highly dynamic applications, it may be necessary to check whether a routeexists before using it to generate a URL. In those cases, don't use thegetRouteCollection() method becausethat regenerates the routing cache and slows down the application.

Instead, try to generate the URL and catch theRouteNotFoundException thrownwhen the route doesn't exist:

use Symfony\Component\Routing\Exception\RouteNotFoundException;

// ...

try {
    $url = $this->router->generate($routeName, $routeParameters);
} catch (RouteNotFoundException $e) {
    // the route is not defined...
}

Forcing HTTPS on Generated URLs

By default, generated URLs use the same HTTP scheme as the current request.In console commands, where there is no HTTP request, URLs use http bydefault. You can change this per command (via the router's getContext()method) or globally with these configuration parameters:

  • YAML
# config/services.yaml
parameters:
    router.request_context.scheme: 'https'
    asset.request_context.secure: true
  • XML
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <parameters>
        <parameter key="router.request_context.scheme">https</parameter>
        <parameter key="asset.request_context.secure">true</parameter>
    </parameters>

</container>
  • PHP
// config/services.php
$container->setParameter('router.request_context.scheme', 'https');
$container->setParameter('asset.request_context.secure', true);

Outside of console commands, use the schemes option to define the scheme ofeach route explicitly:

  • Annotations
// src/Controller/MainController.php
namespace App\Controller;

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

class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="login", schemes={"https"})
     */
    public function login()
    {
        // ...
    }
}
  • YAML
# config/routes.yaml
login:
    path:       /login
    controller: App\Controller\SeurityController::login
    schemes:    [https]
  • XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="login" path="/login" schemes="https"
           controller="App\Controller\SecurityController::login"/>
</routes>
  • PHP
// config/routes.php
use App\Controller\SecurityController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add('login', '/login')
        ->controller([SecurityController::class, 'login'])
        ->schemes(['https'])
    ;
};

The URL generated for the login route will always use HTTPS. This means thatwhen using the path() Twig function to generate URLs, you may get anabsolute URL instead of a relative URL if the HTTP scheme of the originalrequest is different from the scheme used by the route:

{# if the current scheme is HTTPS, generates a relative URL: /login #}
{{ path('login') }}

{# if the current scheme is HTTP, generates an absolute URL to change
   the scheme: https://example.com/login #}
{{ path('login') }}

The scheme requirement is also enforced for incoming requests. If you try toaccess the /login URL with HTTP, you will automatically be redirected to thesame URL, but with the HTTPS scheme.

If you want to force a group of routes to use HTTPS, you can define the defaultscheme when importing them. The following example forces HTTPS on all routesdefined as annotations:

  • YAML
# config/routes/annotations.yaml
controllers:
    resource: '../src/Controller/'
    type: annotation
    defaults:
        schemes: [https]
  • XML
<!-- config/routes/annotations.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="../src/Controller/" type="annotation">
        <default locale="schemes">HTTPS</prefix>
    </import>
</routes>
  • PHP
// config/routes/annotations.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->import('../src/Controller/', 'annotation')
        ->schemes(['https'])
    ;
};

Note

The Security component providesanother way to enforce HTTP or HTTPSvia the requires_channel setting.

Troubleshooting

Here are some common errors you might see while working with routing:

Controller "App\Controller\BlogController::show()" requires that youprovide a value for the "$slug" argument.

This happens when your controller method has an argument (e.g. $slug):

public function show($slug)
{
    // ...
}

But your route path does not have a {slug} parameter (e.g. it is/blog/show). Add a {slug} to your route path: /blog/show/{slug} orgive the argument a default value (i.e. $slug = null).

Some mandatory parameters are missing ("slug") to generate a URL for route"blog_show".

This means that you're trying to generate a URL to the blogshow route butyou are _not passing a slug value (which is required, because it has a{slug} parameter in the route path). To fix this, pass a slug value whengenerating the route:

$this->generateUrl('blog_show', ['slug' => 'slug-value']);

// or, in Twig
// {{ path('blog_show', {slug: 'slug-value'}) }}

Learn more about Routing