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();
andremaha

About The Author

No Comments

Leave A Reply