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.
No Comments