.. index:: single: Routing; Optional Placeholders
To make things more exciting, add a new route that displays a list of all the available blog posts for this imaginary blog application:
.. configuration-block:: .. code-block:: php-annotations // src/Controller/BlogController.php // ... class BlogController extends AbstractController { // ... /** * @Route("/blog") */ public function index() { // ... } } .. code-block:: yaml # config/routes.yaml blog: path: /blog controller: App\Controller\BlogController::index .. code-block:: 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 http://symfony.com/schema/routing/routing-1.0.xsd"> <route id="blog" path="/blog"> <default key="_controller">App\Controller\BlogController::index</default> </route> </routes> .. code-block:: php // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('blog', new Route('/blog', [ '_controller' => 'App\Controller\BlogController::index', ])); return $routes;
So far, this route is as simple as possible - it contains no placeholders
and will only match the exact URL /blog
. But what if you need this route
to support pagination, where /blog/2
displays the second page of blog
entries? Update the route to have a new {page}
placeholder:
.. configuration-block:: .. code-block:: php-annotations // src/Controller/BlogController.php // ... /** * @Route("/blog/{page}") */ public function index($page) { // ... } .. code-block:: yaml # config/routes.yaml blog: path: /blog/{page} controller: App\Controller\BlogController::index .. code-block:: 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 http://symfony.com/schema/routing/routing-1.0.xsd"> <route id="blog" path="/blog/{page}"> <default key="_controller">App\Controller\BlogController::index</default> </route> </routes> .. code-block:: php // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('blog', new Route('/blog/{page}', [ '_controller' => 'App\Controller\BlogController::index', ])); return $routes;
Like the {slug}
placeholder before, the value matching {page}
will
be available inside your controller. Its value can be used to determine which
set of blog posts to display for the given page.
But hold on! Since placeholders are required by default, this route will
no longer match on simply /blog
. Instead, to see page 1 of the blog,
you'd need to use the URL /blog/1
! Since that's no way for a rich web
app to behave, modify the route to make the {page}
parameter optional.
This is done by including it in the defaults
collection:
.. configuration-block:: .. code-block:: php-annotations // src/Controller/BlogController.php // ... /** * @Route("/blog/{page}", defaults={"page"=1}) */ public function index($page) { // ... } .. code-block:: yaml # config/routes.yaml blog: path: /blog/{page} controller: App\Controller\BlogController::index defaults: { page: 1 } .. code-block:: 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 http://symfony.com/schema/routing/routing-1.0.xsd"> <route id="blog" path="/blog/{page}"> <default key="_controller">App\Controller\BlogController::index</default> <default key="page">1</default> </route> </routes> .. code-block:: php // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('blog', new Route('/blog/{page}', [ '_controller' => 'App\Controller\BlogController::index', 'page' => 1, ])); return $routes;
By adding page
to the defaults
key, the {page}
placeholder is
no longer required. The URL /blog
will match this route and the value
of the page
parameter will be set to 1
. The URL /blog/2
will
also match, giving the page
parameter a value of 2
. Perfect.
URL | Route | Parameters |
---|---|---|
/blog |
blog |
{page} = 1 |
/blog/1 |
blog |
{page} = 1 |
/blog/2 |
blog |
{page} = 2 |
Caution!
You can have more than one optional placeholder (e.g. /blog/{slug}/{page}
),
but everything after an optional placeholder must be optional. For example,
/{page}/blog
is a valid path, but page
will always be required
(i.e. simply /blog
will not match this route).
Tip
Routes with optional parameters at the end will not match on requests
with a trailing slash (i.e. /blog/
will not match, /blog
will match).