Advertisement
  1. Code
  2. JavaScript

Set Up Routing in PHP Applications Using the Symfony Routing Component

Scroll to top

What Is the Symfony Routing Component?

The Symfony Routing Component is a very popular Routing component which is adapted by several frameworks and provides a lot of flexibility should you wish to set up routes in your PHP application.

If you've built a custom PHP application and are looking for a feature-rich routing library, the Symfony Routing component is one of the best candidates. It also allows you to define routes for your application in the YAML format.

Starting with installation and configuration, we'll go through real-world examples to demonstrate a variety of options the component has for route configuration. In this article, you'll learn:

Installation and Configuration

In this section, we're going to install the libraries that are required in order to set up routing in your PHP applications. I assume that you've installed Composer in your system as we'll need it to install the necessary libraries that are available on Packagist.

Once you've installed Composer, go ahead and install the core Routing component using the following command.

1
$composer require symfony/routing

Although the Routing component itself is sufficient to provide comprehensive routing features in your application, we'll go ahead and install a few other components as well to make our life easier and enrich the existing core routing functionality.

To start with, we'll go ahead and install the HttpFoundation component, which provides an object-oriented wrapper for PHP global variables and response-related functions. It makes sure that you don't need to access global variables like $_GET, $_POST and the like directly.

1
$composer require symfony/http-foundation

Next, if you want to define your application routes in the YAML file instead of the PHP code, it's the YAML component which comes to the rescue as it helps you to convert YAML strings to PHP arrays and vice versa.

1
$composer require symfony/yaml

Finally, we'll install the Config component, which provides several utility classes to initialize and deal with configuration values defined in the different types of file like YAML, INI, XML, etc. In our case, we'll use it to load routes from the YAML file.

1
$composer require symfony/config

So that's the installation part, but how are you supposed to use it? In fact, it's just a matter of including the autoload.php file created by Composer in your application, as shown in the following snippet.

1
<?php
2
require_once './vendor/autoload.php';
3
 
4
// application code

5
?>

Set Up Basic Routes

In the previous section, we went through the installation of the necessary routing components. Now, you're ready to set up routing in your PHP application right away.

Let's go ahead and create the basic_routes.php file with the following contents.

1
<?php
2
require_once './vendor/autoload.php';
3
 
4
use Symfony\Component\Routing\Matcher\UrlMatcher;
5
use Symfony\Component\Routing\RequestContext;
6
use Symfony\Component\Routing\RouteCollection;
7
use Symfony\Component\Routing\Route;
8
use Symfony\Component\HttpFoundation\Request;
9
use Symfony\Component\Routing\Generator\UrlGenerator;
10
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
11
 
12
try
13
{
14
    // Init basic route

15
    $foo_route = new Route(
16
      '/foo',
17
      array('controller' => 'FooController')
18
    );
19
 
20
    // Init route with dynamic placeholders

21
    $foo_placeholder_route = new Route(
22
      '/foo/{id}',
23
      array('controller' => 'FooController', 'method'=>'load'),
24
      array('id' => '[0-9]+')
25
    );
26
 
27
    // Add Route object(s) to RouteCollection object

28
    $routes = new RouteCollection();
29
    $routes->add('foo_route', $foo_route);
30
    $routes->add('foo_placeholder_route', $foo_placeholder_route);
31
 
32
    // Init RequestContext object

33
    $context = new RequestContext();
34
    $context->fromRequest(Request::createFromGlobals());
35
 
36
    // Init UrlMatcher object

37
    $matcher = new UrlMatcher($routes, $context);
38
 
39
    // Find the current route

40
    $parameters = $matcher->match($context->getPathInfo());
41
 
42
    // How to generate a SEO URL

43
    $generator = new UrlGenerator($routes, $context);
44
    $url = $generator->generate('foo_placeholder_route', array(
45
      'id' => 123,
46
    ));
47
 
48
    echo '<pre>';
49
    print_r($parameters);
50
 
51
    echo 'Generated URL: ' . $url;
52
    exit;
53
}
54
catch (ResourceNotFoundException $e)
55
{
56
  echo $e->getMessage();
57
}

Setting up routing using the Symfony Routing component usually goes through a series of steps as listed below.

  • Initialize the Route object for each of your application routes.
  • Add all Route objects to the RouteCollection object.
  • Initialize the RequestContext object which holds the current request context information.
  • Initialize the UrlMatcher object by passing the RouteCollection object and the RequestContext object.

Initialize the Route Object for Different Routes

Let's go ahead and define a pretty basic foo route.

1
$foo_route = new Route(
2
  '/foo',
3
  array('controller' => 'FooController')
4
);

The first argument of the Route constructor is the URI path, and the second argument is the array of custom attributes that you want to return when this particular route is matched. Typically, it would be a combination of the controller and method which you would like to call when this route is requested.

Next, let's have a look at the parameterized route.

1
$foo_placeholder_route = new Route(
2
  '/foo/{id}',
3
  array('controller' => 'FooController', 'method'=>'load'),
4
  array('id' => '[0-9]+')
5
);

The above route can match URIs like foo/1, foo/123 and similar. Please note that we've restricted the {id} parameter to numeric values only, and hence it won't match URIs like foo/bar since the {id} parameter is provided as a string.

Add All Route Objects to the RouteCollection Object

The next step is to add route objects that we've initialized in the previous section to the RouteCollection object.

1
$routes = new RouteCollection();
2
$routes->add('foo_route', $foo_route);
3
$routes->add('foo_placeholder_route', $foo_placeholder_route);

As you can see, it's pretty straightforward as you just need to use the add method of the RouteCollection object to add route objects. The first argument of the add method is the name of the route, and the second argument is the route object itself.

Initialize the RequestContext Object

Next, we need to initialize the RequestContext object, which holds the current request context information. We'll need this object when we initialize the UrlMatcher object as we'll go through it in a moment.

1
$context = new RequestContext();
2
$context->fromRequest(Request::createFromGlobals());

Initialize the UrlMatcher Object

Finally, we need to initialize the UrlMatcher object along with routes and context information.

1
// Init UrlMatcher object

2
$matcher = new UrlMatcher($routes, $context);

Now, we have everything we could match our routes against.

How to Match Routes

It's the match method of the UrlMatcher object which allows you to match any route against a set of predefined routes.

The match method takes the URI as its first argument and tries to match it against predefined routes. If the route is found, it returns custom attributes associated with that route. On the other hand, it throws the ResourceNotFoundException exception if there's no route associated with the current URI.

1
$parameters = $matcher->match($context->getPathInfo());

In our case, we've provided the current URI by fetching it from the $context object. So, if you're accessing the https://your-domain/basic_routes.php/foo URL, the $context->getPathInfo() returns foo, and we've already defined a route for the foo URI, so it should return us the following.

1
Array
2
(
3
    [controller] => FooController
4
    [_route] => foo_route
5
)

Now, let's go ahead and test the parameterized route by accessing the http://your-domain/basic_routes.php/foo/123 URL.

1
Array
2
(
3
    [controller] => FooController
4
    [method] => load
5
    [id] => 123
6
    [_route] => foo_placeholder_route
7
)

It worked if you can see that the id parameter is bound with the appropriate value 123.

Next, let's try to access a non-existent route like http://your-domain/basic_routes.php/unknown-route, and you should see the following message.

1
No routes found for "/unknown-route".

So that's how you can find routes using the match method.

Apart from this, you could also use the Routing component to generate links in your application. Provided RouteCollection and RequestContext objects, the UrlGenerator allows you to build links for specific routes.

1
$generator = new UrlGenerator($routes, $context);
2
$url = $generator->generate('foo_placeholder_route', array(
3
  'id' => 123,
4
));

The first argument of the generate method is the route name, and the second argument is the array which may contain parameters if it's the parameterized route. The above code should generate the /basic_routes.php/foo/123 URL.

Load Routes From the YAML File

In the previous section, we built our custom routes using the Route and RouteCollection objects. In fact, the Routing component offers different ways you could choose from to instantiate routes. You could choose from various loaders like YamlFileLoader, XmlFileLoader, and PhpFileLoader.

In this section, we'll go through the YamlFileLoader loader to see how to load routes from the YAML file.

The Routes YAML File

Go ahead and create the routes.yaml file with the following contents.

1
foo_route:
2
    path:     /foo
3
    controller: App\Controller\FooController::index
4
    methods:    GET
5
 
6
foo_placeholder_route:
7
    path:     /foo/{id}
8
    controller: App\Controller\FooController::load
9
    methods:    GET
10
    requirements:
11
        id: '[0-9]+'

An Example File

Next, go ahead and make the load_routes_from_yaml.php file with the following contents.

1
<?php
2
require_once './vendor/autoload.php';
3
 
4
use Symfony\Component\Routing\Matcher\UrlMatcher;
5
use Symfony\Component\Routing\RequestContext;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\Routing\Generator\UrlGenerator;
8
use Symfony\Component\Config\FileLocator;
9
use Symfony\Component\Routing\Loader\YamlFileLoader;
10
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
11
 
12
try
13
{
14
    // Load routes from the yaml file

15
    $fileLocator = new FileLocator(array(__DIR__));
16
    $loader = new YamlFileLoader($fileLocator);
17
    $routes = $loader->load('routes.yaml');
18
 
19
    // Init RequestContext object

20
    $context = new RequestContext();
21
    $context->fromRequest(Request::createFromGlobals());
22
 
23
    // Init UrlMatcher object

24
    $matcher = new UrlMatcher($routes, $context);
25
 
26
    // Find the current route

27
    $parameters = $matcher->match($context->getPathInfo());
28
 
29
    // How to generate a SEO URL

30
    $generator = new UrlGenerator($routes, $context);
31
    $url = $generator->generate('foo_placeholder_route', array(
32
      'id' => 123,
33
    ));
34
 
35
    echo '<pre>';
36
    print_r($parameters);
37
 
38
    echo 'Generated URL: ' . $url;
39
    exit;
40
}
41
catch (ResourceNotFoundException $e)
42
{
43
  echo $e->getMessage();
44
}

The only thing that's different in this case is the way we initialize routes!

1
$fileLocator = new FileLocator(array(__DIR__));
2
$loader = new YamlFileLoader($fileLocator);
3
$routes = $loader->load('routes.yaml');

We've used the YamlFileLoader loader to load routes from the routes.yaml file instead of initializing it directly in the PHP itself. Apart from that, everything is the same and should produce the same results as that of the basic_routes.php file.

The All-in-One Router

In this section, we'll go through the Router class, which allows you to set up routing quickly with fewer lines of code.

Go ahead and make the all_in_one_router.php file with the following contents.

1
<?php
2
require_once './vendor/autoload.php';
3
 
4
use Symfony\Component\Routing\RequestContext;
5
use Symfony\Component\Routing\Router;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\Routing\Generator\UrlGenerator;
8
use Symfony\Component\Config\FileLocator;
9
use Symfony\Component\Routing\Loader\YamlFileLoader;
10
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
11
 
12
try
13
{
14
    $fileLocator = new FileLocator(array(__DIR__));
15
 
16
    $requestContext = new RequestContext();
17
    $requestContext->fromRequest(Request::createFromGlobals());
18
 
19
    $router = new Router(
20
        new YamlFileLoader($fileLocator),
21
        'routes.yaml',
22
        array('cache_dir' => __DIR__.'/cache'),
23
        $requestContext
24
    );
25
 
26
    // Find the current route

27
    $parameters = $router->match($requestContext->getPathInfo());
28
 
29
    // How to generate a SEO URL

30
    $routes = $router->getRouteCollection();
31
    $generator = new UrlGenerator($routes, $requestContext);
32
    $url = $generator->generate('foo_placeholder_route', array(
33
      'id' => 123,
34
    ));
35
 
36
    echo '<pre>';
37
    print_r($parameters);
38
 
39
    echo 'Generated URL: ' . $url;
40
    exit;
41
}
42
catch (ResourceNotFoundException $e)
43
{
44
  echo $e->getMessage();
45
}

Everything is pretty much the same, except that we've instantiated the Router object along with the necessary dependencies.

1
$router = new Router(
2
    new YamlFileLoader($fileLocator),
3
    'routes.yaml',
4
    array('cache_dir' => __DIR__.'/cache'),
5
    $requestContext
6
);

With that in place, you can straight away use the match method of the Router object for route mapping.

1
$parameters = $router->match($requestContext->getPathInfo());

Also, you will need to use the getRouteCollection method of the Router object to fetch routes.

1
$routes = $router->getRouteCollection();

In this section, we'll discuss how you can implement annotation-based routing. It's becoming one of the most popular ways to define routes among different frameworks.

Before we go ahead and implement annotation-based routing, we need to install a couple of packages. Let's do it quickly as shown in the following snippet.

1
$composer require symfony/framework-bundle
2
$composer require doctrine/annotations
3
$composer require doctrine/cache

As you can see, we've installed three different components.

In your composer.json file, add the following:

1
"autoload": {
2
    "psr-4": {
3
        "App\\": "app/"
4
    }
5
}

Now, run the following.

1
$composer dump-autoload

Now, we are ready to prepare the files.

Go ahead and create the index.php file with the following contents.

1
<?php
2
use Doctrine\Common\Annotations\AnnotationReader;
3
use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader;
4
use Symfony\Component\Config\FileLocator;
5
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
6
use Composer\Autoload\ClassLoader;
7
use Doctrine\Common\Annotations\AnnotationRegistry;
8
9
use Symfony\Component\Routing\RequestContext;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\Routing\Matcher\UrlMatcher;
12
13
/** @var ClassLoader $loader */
14
$loader = require __DIR__ . '/vendor/autoload.php';
15
16
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
17
18
$loader = new AnnotationDirectoryLoader(
19
    new FileLocator(__DIR__ . '/src/Controller/'),
20
    new AnnotatedRouteControllerLoader(
21
        new AnnotationReader()
22
    )
23
);
24
25
$routes = $loader->load(__DIR__ . '/src/Controller/');
26
$context = new RequestContext();
27
$context->fromRequest(Request::createFromGlobals());
28
29
$matcher = new UrlMatcher($routes, $context);
30
$parameters = $matcher->match($context->getPathInfo());
31
32
$controllerInfo = explode('::',$parameters['_controller']);
33
34
$controller = new $controllerInfo[0];
35
$action = $controllerInfo[1];
36
37
$controller->$action();

Now, let's create the controller file at src/Controller/FooController.php with the following contents.

1
<?php
2
namespace App\Controller;
3
4
use Symfony\Component\Routing\Annotation\Route;
5
6
class DefaultController
7
{
8
    /**

9
     * @Route("/",name="index")

10
     */
11
    public function index()
12
    {
13
        echo "Index action";
14
    }
15
16
    /**

17
     * @Route("/hello",name="hello")

18
     */
19
    public function hello()
20
    {
21
        echo "Hello action";
22
    }
23
}

You may have noticed that we've defined routes for each method in the form of an annotation. The benefit of this approach is that it allows you to define routes next to the code of the controllers associated with those routes.

Go ahead and access the https://your-domain/index.php/ URL. As per the following routing configuration, it should call the index method.

1
/**
2
 * @Route("/",name="index")
3
 */

On the other hand, if you try to access the http://your-domain/index.php/hello URL, it should call the hello method of the DefaultController controller class.

So that's how annotation-based routing works!

Conclusion

Go ahead and explore the other options available in the Routing component.

Today, we explored the Symfony Routing component, which makes implementation of routing in PHP applications a breeze. Along the way, we created a handful of examples to demonstrate various aspects of the Routing component. 

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.