模板
聪明的读者已经注意到了,我们框架中具体代码(模板)的运行方式是硬编码的。对于至今我们创建过的那些简单页面来说,这是没有问题的,但是如果你想添加更多的逻辑,你只能强制将逻辑代码放进模板里面。特别是如果你的脑中仍然记得关注点分离的准则的话,你会知道这不是一个好主意。
让我们通过添加一个新的层来将模板的代码从逻辑中分离出来: 控制器的目的是基于来自客户端请求的信息生成一个响应。
像下面一样改变框架的模板渲染部分:
// example.com/web/front.php
// ...
try {
$request->attributes->add($matcher->match($request->getPathInfo()));
$response = call_user_func('render_template', $request);
} catch (Routing\Exception\ResourceNotFoundException $e) {
$response = new Response('Not Found', 404);
} catch (Exception $e) {
$response = new Response('An error occurred', 500);
}
现在模板渲染由内部函数(render_template()
)实现,我们需要传递给该函数从URL中提取的属性。我们可以通过一个额外的参数传递这些属性,但是让我们换个做法,使用Request
的另一个被称作属性的特性:请求属性是一种获取请求的一些额外信息,这些信息与HTTP请求数据不直接相关。
你现在创建了 render_template()
函数,一个通过的控制器,可以去渲染一个没有具体逻辑的模板。为了保持和之前相同的模板,请求属性在模板渲染前就被提取了出来:
function render_template($request)
{
extract($request->attributes->all(), EXTR_SKIP);
ob_start();
include sprintf(__DIR__.'/../src/pages/%s.php', $_route);
return new Response(ob_get_clean());
}
之前 render_template
作为PHP call_user_func()
的一个参数来使用,现在我们可以用任何合法的PHP回调函数来取代它。这允许我们去使用一个函数来当做一个控制器,匿名函数或者类的方法...你自己选择。
通常的, 对于每一个路由,它所关联的控制器由_controller
路由属性配置:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => 'render_template',
)));
try {
$request->attributes->add($matcher->match($request->getPathInfo()));
$response = call_user_func($request->attributes->get('_controller'), $request);
} catch (Routing\Exception\ResourceNotFoundException $e) {
$response = new Response('Not Found', 404);
} catch (Exception $e) {
$response = new Response('An error occurred', 500);
}
一个路由现在可以关联任何的控制器,当然您仍然可以在一个控制器中使用render_template()
来渲染一个模板:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => function ($request) {
return render_template($request);
}
)));
这是相当灵活的,因为你现在既可以在之后改变响应的对象,甚至还可以传递给模板额外的参数:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => function ($request) {
// $foo will be available in the template
$request->attributes->set('foo', 'bar');
$response = render_template($request);
// change some header
$response->headers->set('Content-Type', 'text/plain');
return $response;
}
)));
下面就是我们框架更新后的增强版本:
// example.com/web/front.php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing;
function render_template($request)
{
extract($request->attributes->all(), EXTR_SKIP);
ob_start();
include sprintf(__DIR__.'/../src/pages/%s.php', $_route);
return new Response(ob_get_clean());
}
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';
$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
try {
$request->attributes->add($matcher->match($request->getPathInfo()));
$response = call_user_func($request->attributes->get('_controller'), $request);
} catch (Routing\Exception\ResourceNotFoundException $e) {
$response = new Response('Not Found', 404);
} catch (Exception $e) {
$response = new Response('An error occurred', 500);
}
$response->send();
为了庆祝我们新框架的诞生,让我们来创建一个带有一些简单逻辑的全新框架吧。访问/is_leap_year
,你可以得到现在的年份,同时链接后面带明确的年份比如/is_leap_year/2009
。我们的框架不再需要任何方式的修改了,只需要创建一个新的app.php
文件:
// example.com/src/app.php
use Symfony\Component\Routing;
use Symfony\Component\HttpFoundation\Response;
function is_leap_year($year = null) {
if (null === $year) {
$year = date('Y');
}
return 0 === $year % 400 || (0 === $year % 4 && 0 !== $year % 100);
}
$routes = new Routing\RouteCollection();
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
'year' => null,
'_controller' => function ($request) {
if (is_leap_year($request->attributes->get('year'))) {
return new Response('Yep, this is a leap year!');
}
return new Response('Nope, this is not a leap year.');
}
)));
return $routes;
is_leap_year()
函数会判断给定的年份是不是闰年,是的话返回 true
,反之返回false
。如果年份是 null
, 会使用当前年份去判断。控制器很简单:它从请求属性中获取年份,将它创递给is_leap_year()
函数,然后根据返回值创建一个新的响应对象。
如往常一样,你可以决定就此止步,使用现在的这个框架;也许你需要的就是去创建一些简单的网站,就像这些很棒的网站或者其他的一些。