Today, we'll go through the Symfony Routing component, which allows you to set up routing in your PHP applications.
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 more than a worth a look. 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
- how to set up basic routes
- how to load routes from the YAML file
- how to use the all-in-one router
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.
$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.
$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 that comes to the rescue as it helps you to convert YAML strings to PHP arrays and vice versa.
$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.
$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.
<?php require_once './vendor/autoload.php'; // application code ?>
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.
<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { // Init basic route $foo_route = new Route( '/foo', array('controller' => 'FooController') ); // Init route with dynamic placeholders $foo_placeholder_route = new Route( '/foo/{id}', array('controller' => 'FooController', 'method'=>'load'), array('id' => '[0-9]+') ); // Add Route object(s) to RouteCollection object $routes = new RouteCollection(); $routes->add('foo_route', $foo_route); $routes->add('foo_placeholder_route', $foo_placeholder_route); // Init RequestContext object $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); // Init UrlMatcher object $matcher = new UrlMatcher($routes, $context); // Find the current route $parameters = $matcher->match($context->getPathInfo()); // How to generate a SEO URL $generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
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 theRouteCollection
object. - Initialize the
RequestContext
object which holds the current request context information. - Initialize the
UrlMatcher
object by passing theRouteCollection
object and theRequestContext
object.
Initialize the Route Object for Different Routes
Let's go ahead and define a pretty basic foo
route.
$foo_route = new Route( '/foo', array('controller' => 'FooController') );
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 that you would like to call when this route is requested.
Next, let's have a look at the parameterized route.
$foo_placeholder_route = new Route( '/foo/{id}', array('controller' => 'FooController', 'method'=>'load'), array('id' => '[0-9]+') );
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.
$routes = new RouteCollection(); $routes->add('foo_route', $foo_route); $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.
$context = new RequestContext(); $context->fromRequest(Request::createFromGlobals());
Initialize the UrlMatcher
Object
Finally, we need to initialize the UrlMatcher
object along with routes and context information.
// Init UrlMatcher object $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.
$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 http://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.
Array ( [controller] => FooController [_route] => foo_route )
Now, let's go ahead and test the parameterized route by accessing the http://your-domain/basic_routes.php/foo/123 URL.
Array ( [controller] => FooController [method] => load [id] => 123 [_route] => foo_placeholder_route )
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.
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.
$generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, ));
The first argument of the generate
method is the route name, and the second argument is the array that 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.
foo_route: path: /foo defaults: { controller: 'FooController::indexAction' } foo_placeholder_route: path: /foo/{id} defaults: { controller: 'FooController::loadAction' } requirements: id: '[0-9]+'
An Example File
Next, go ahead and make the load_routes_from_yaml.php file with the following contents.
<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { // Load routes from the yaml file $fileLocator = new FileLocator(array(__DIR__)); $loader = new YamlFileLoader($fileLocator); $routes = $loader->load('routes.yaml'); // Init RequestContext object $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); // Init UrlMatcher object $matcher = new UrlMatcher($routes, $context); // Find the current route $parameters = $matcher->match($context->getPathInfo()); // How to generate a SEO URL $generator = new UrlGenerator($routes, $context); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
The only thing that's different in this case is the way we initialize routes!
$fileLocator = new FileLocator(array(__DIR__)); $loader = new YamlFileLoader($fileLocator); $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
Lastly 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.
<?php require_once './vendor/autoload.php'; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Router; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Exception\ResourceNotFoundException; try { $fileLocator = new FileLocator(array(__DIR__)); $requestContext = new RequestContext(); $requestContext->fromRequest(Request::createFromGlobals()); $router = new Router( new YamlFileLoader($fileLocator), 'routes.yaml', array('cache_dir' => __DIR__.'/cache'), $requestContext ); // Find the current route $parameters = $router->match($requestContext->getPathInfo()); // How to generate a SEO URL $routes = $router->getRouteCollection(); $generator = new UrlGenerator($routes, $requestContext); $url = $generator->generate('foo_placeholder_route', array( 'id' => 123, )); echo '<pre>'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
Everything is pretty much the same, except that we've instantiated the Router
object along with the necessary dependencies.
$router = new Router( new YamlFileLoader($fileLocator), 'routes.yaml', array('cache_dir' => __DIR__.'/cache'), $requestContext );
With that in place, you can straight away use the match
method of the Router object for route mapping.
$parameters = $router->match($requestContext->getPathInfo());
Also, you will need to use the getRouteCollection
method of the Router object to fetch routes.
$routes = $router->getRouteCollection();
Conclusion
Go ahead and explore the other options available in the Routing component—I would love to hear your thoughts!
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.
I hope that you've enjoyed this article, and feel free to post your thoughts using the feed below!
No comments:
Post a Comment