Symfony routes with conditions

coding010
2 min readJun 8, 2022

--

I was working on a recent Symfony project and had a requirement where the response between routes /foo and /foo?var=bar should be totally different. Of course this can be done in many ways in Symfony, but I struggled a bit with how to do this properly and clean. In this short blog post I’ll explain the solution I went for.

A Symfony Controller class

This solution requires us to install the Symfony ExpressionLanguage Component. I’ll show you why in the code examples down below.

Let’s install the ExpressionLanguage Component:

composer require symfony/expression-language

The ExpressionLanguage Component is pretty powerful and can do much more than this simple example.

Then create a Controller class for the /foo route:

// src/Controller/DefaultController
<?php

namespace App\Controller;

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

class DefaultController extends AbstractController
{
#[Route('/foo', name: 'app.no_var')]
public function __invoke(): Response
{
// Some specific logic goes here

// ... and then return the response
return $this->json([
'name' => 'John Doe',
]);
}
}

And create a second Controller class for /foo?var=bar:

// src/Controller/VarController
<?php

namespace App\Controller;

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

class VarController extends AbstractController
{
#[Route(
'/foo',
name: 'app.with_var',
condition: "'bar' === request.query.get('var')",
priority: 1,
)]
public function __invoke(): Response
{
// Some totally different logic goes here
$transport = '🚗';

// ... and then return the response,
return $this->render('var/index.html.twig', [
'transport' => $transport,
]);
}
}
  • The key is the condition option in the Route. This makes matching of the route based on arbitrary matching logic. In our case a GET variable named var with the value of bar. Without the ExpressionLanguage Component we installed this would not be possible.
  • The other important thing to mention is the priority: 1 option on this Route. It makes the priority of this route higher than the route defined in DefaultController.

I’m using attributes, but you can define your routes in XML, PHP or YAML as well. How to do that is out of scope for this post.

We can see our newly defined routes and their order when running the debug:router command.

symfony console debug:router

Which outputs the all routes in their matching order:

---------------- -------- -------- ------ -----
Name Method Scheme Host Path
---------------- -------- -------- ------ -----
app.with_var ANY ANY ANY /foo
app.no_var ANY ANY ANY /foo
---------------- -------- -------- ------ -----

I tried other solutions like forward (heavy) and a kernel.controller event to change the controller to solve the problem. But it all seemed more complex and less obvious than this solution.

Please let me know your thoughts. There are many solutions and maybe you have a better way to do this.

--

--