Screencast Series – Creating Own PHP Framework Using Symfony2 Components – Episode 4
Gist

In this episode we’ll use the Symfony2 Component Routing, which will take care of mapping the URLs to the file paths. One of the advantages of the Routing component is the ability to create dynamical paths. To convert something like /hello?name=Andrey into /name/Andrey we’ll take advantage of RouteCollection, URLMatcher and RequestConext – the classes bundled with the Routing component.
Screencast
Show Notes
Refactoring
Let our controller take care of the variables, so the templates get even slimmer. Using extract() function to get an associative array out of the globals. So the $_GET['name'] = Andrey will be converted into $name = Andrey
<?php
// /web/front.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$map = array(
'/hello' => 'hello',
'/bye' => 'bye',
);
$path = $request->getPathInfo();
if (isset($map[$path])) {
ob_start();
extract($request->query->all(), EXTR_SKIP);
include sprintf(__DIR__.'/../src/pages/%s.php', $map[$path]);
$response = new Response(ob_get_clean());
} else {
$response = new Response('Not Found', 404);
}
$response->send();
// /src/pages/hello.php Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>
Routing Component
Although we’ve created a simple mapping that allowed to map URLs to the files – it was not flexible enough. Instead of /hello?name=Andrey we’ll be creating something much sexier, a dynamic path – /hello/Andrey. And yes, it is SEO-friendly.
First, we’ll tell a composer.json to add a new dependency
// composer.json
{
"require": {
"symfony/class-loader": "2.1.*",
"symfony/http-foundation": "2.1.*",
"symfony/routing": "2.1.*"
}
}
and run the update to install it
php composer.phar update
Second, we’ll get rid of our home-brewed autoloader in favor of the autoloader that comes bundled with the Composer library. Reason? Well, we’ll be adding even more Symfony2 Components in the course of the series, and each time we do – autoloader will update itself. Pretty handy!
<?php // /web/front.php require_once __DIR__.'/../vendor/.composer/autoload.php'; ...
Third, we’ll replace our mapping accusative array with the combination of three classed included in the Routing component: Route Collection, RequestContext and UrlMatcher.
Route Collection
We’ll add the instance of the RouteCollection in the application file:
<?php
// /src/app.php
use Symfony\Component\Routing;
$routes = new Routing\RouteCollection();
$routes->add('hello', new Routing\Route('/hello/{name}', array('name' => 'World')));
$routes->add('bye', new Routing\Route('/bye'));
return $routes;
UrlMathcher and RequestContext
Based on the information stored in the RouteCollection the UrlMatcher can take the URL and translate it into the file path. It takes two parameters – the RouteCollection and RequestContext.
The updated version of the controller will look like that:
// /web/front.php
require_once __DIR__.'/../vendor/.composer/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing;
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';
$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
try {
extract($matcher->match($request->getPathInfo()), EXTR_SKIP);
ob_start();
include sprintf(__DIR__.'/../src/pages/%s.php', $_route);
$response = new Response(ob_get_clean());
} catch (Routing\Exception\ResourceNotFoundException $e) {
$response = new Response('Not Found', 404);
} catch (Exception $e) {
$response = new Response('An error occurred', 500);
}
$response->send();
No Comments