Screencast Series – Creating Own PHP Framework Using Symfony2 Components – Episode 5

Gist

Today we’ll cover one of the corner stones of modern application architecture – the controller. Its mission is to generate a Response based on the information provided by the client Request. Put in simplest terms: the controller contains the business logic of your application. Say, a client requests an URL example.com/is_leap_year/2012. Instead of writing everything inside of the file leap_year.php where the presentational markup and programming logic get mixed, we’ll move the leap year calculations into a very digestible container – the controller.

Show notes

Callback function

We’ll use the external function render_template() to take care of the buffer, file inclusion and Response generation

<?php
// /web/front.php

...

// Callback function for rendering templates
function render_template($request)
{
    extract($request->attributes->all(), EXTR_SKIP);
    ob_start();
    require sprintf(__DIR__ . '/../src/pages/%s.php', $_route);

    return new Response(ob_get_clean());
}

...

// Handle Request
try {
    $request->attributes->add($matcher->match($request->getPathInfo()));
    $response = call_user_func('render_template', $request);
}

...

Defining Controller in the Route

Each Route has a conventional key _controller that could be filled, as you might have guessed, with the function to use a controller. We’ll adjust the Route to use this convention

<?php 
// /src/app.php

...

$routes->add('hello', new Route('/hello/{name}', array(
    'name' => 'World',
    '_controller' => 'render_template'
)));

So instead of hard coding the controller name in the front.php we’ll be using the dynamical value that comes from the Route

<?php
// /web/front.php

...

try {
    $request->attributes->add($matcher->match($request->getPathInfo()));
    $response = call_user_func($request->attributes->get('_controller'), $request);
}

Modifying the Response

The Controller is much more then just the refactored way include a template file. Having added the controller we could take the values in the Request, add a couple of our own, render the template and modify the Response to show an entire different content type in the browser – all in one run:

<?php
// /src/app.php

...

$routes->add('hello', new Route('/hello/{name}', array(
    'name' => 'World',
    '_controller' => function ($request) {

        // Add some variables to the template
        $request->attributes->set('age', 28);

        // Render the template
        $response = render_template($request);

        // Change the content type
        $response->headers->set('Content-Type', 'text/plain');

        return $response;

    }
)));

Leap Year

Having build the solid controller we’ll have some fun by extending our application. We’ll build a simple REST-Service to calculate if the year in question provides is leap or not.
Our final application:

<?php
// /src/app.php

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$routes = new RouteCollection;

function is_leap_year($year = null)
{
    if (null === $year) {
        $year = date('Y');
    }

    return 0 == $year % 400 || (0 == $year % 4 && 0 != $year % 100);
}

$routes->add('hello', new Route('/hello/{name}', array(
    'name' => 'World',
    '_controller' => function ($request) {

        // Add some variables to the template
        $request->attributes->set('age', 28);

        // Render the template
        $response = render_template($request);

        // Change the content type
        $response->headers->set('Content-Type', 'text/plain');

        return $response;

    }
)));

$routes->add('bye', new Route('/bye'));

$routes->add('leap_year', new Route('/is_leap_year/{year}', array(
   'year' => null,
    '_controller' => function ($request) {

        if (is_leap_year($request->attributes->get('year'))) {
            return new Response('Yep, the year you have provided is leap!');
        }

        return new Response('Nope, that is not the leap year!');
    }
)));

return $routes;

Materials Used

I have used the post by Fabien Potencier – the creator of Symfony2 Components – and his blog http://fabien.potencier.org/article/54/create-your-own-framework-on-top-of-the-symfony2-components-part-5 to create this screencast.

andremaha

About The Author

No Comments

Leave A Reply