如何使用PHP模板而不是Twig

3.4 版本
维护中的版本

Symfony默认的模板引擎是twig,但如果你想要,你仍然可以使用纯php代码。这两个模板引擎在symfony中都被支持。Symfony 在 PHP 上添加了一些不错的特性,使得使用 PHP 来编写模板更强大。

如果你选择不使用Twig并禁用它,你需要通过kernel.exception事件,实现你自己的异常处理程序。

渲染PHP模板 

如果你想要去使用php模板引擎,首先,要确信你的应用配置文件已经开启了它:

1
2
3
4
5
# app/config/config.yml
framework:
    # ...
    templating:
        engines: ['twig', 'php']
1
2
3
4
5
6
7
8
<!-- app/config/config.xml -->
<framework:config>
    <!-- ... -->
    <framework:templating>
        <framework:engine id="twig" />
        <framework:engine id="php" />
    </framework:templating>
</framework:config>
1
2
3
4
5
6
$container->loadFromExtension('framework', array(
    // ...
    'templating' => array(
        'engines' => array('twig', 'php'),
    ),
));

你现在直接通过使用.php来替换.twig为后缀的模板名,来渲染PHP模板以替代Twig。下面的控制器渲染的就是index.html.php模板:

1
2
3
4
5
6
7
8
9
10
// src/AppBundle/Controller/HelloController.php
 
// ...
public function indexAction($name)
{
    return $this->render(
        'AppBundle:Hello:index.html.php',
        array('name' => $name)
    );
}

你也可以使用 @Template 快捷方式去渲染默认的AppBundle:Hello:index.html.php模板:

1
2
3
4
5
6
7
8
9
10
11
12
// src/AppBundle/Controller/HelloController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
 
// ...
 
/**
 * @Template(engine="php")
 */
public function indexAction($name)
{
    return array('name' => $name);
}

同时使用phptwig模板引擎是被允许的,但它会对你的应用程序产生不良的副作用:Twig 命名空间的 @ 符号将不再支持 render() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
public function indexAction()
{
    // ...
 
    // namespaced templates will no longer work in controllers
    // 在控制器中命名空间模板将不再工作
    $this->render('@App/Default/index.html.twig');
 
    // you must use the traditional template notation
    // 你一定要使用传统的模板表示法
    $this->render('AppBundle:Default:index.html.twig');
}
1
2
3
4
5
6
7
{# inside a Twig template, namespaced templates work as expected #}
{# 在twig内部, 命名空间模板会像预期一样工作 #}
{{ include('@App/Default/index.html.twig') }}
 
{# traditional template notation will also work #}
{# 传统的模板表示法也正常工作 #}
{{ include('AppBundle:Default:index.html.twig') }}

修饰模板 

通常,模板在项目中会共享通用元素,比如:众所周知的头部和底部。在symfony中,这个问题是以另一不同的方式被考虑的:一个模板可以被另一个修饰。

由于 extend() 的调用,index.html.php模板被layout.html.php修饰:

1
2
3
4
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>
 
Hello <?php echo $name ?>!

AppBundle::layout.html.php表达式听起来很熟悉,不是吗?它和引用一个模板使用的是相同的表达式。:: 部分意味着控制器元素为空,所以相应的文件直接存储在 views/ 下。

现在,看一看layout.html.php文件:

1
2
3
4
5
6
<!-- app/Resources/views/layout.html.php -->
<?php $view->extend('::base.html.php') ?>
 
<h1>Hello Application</h1>
 
<?php $view['slots']->output('_content') ?>

这个layout本身被另一个(::base.html.php)修饰。symfony支持多个修饰(decoration)级别:一个layout本身可以被另一个修饰。当bundle部分的模板名称为空时,视图会在app/Resources/views/目录下寻找。这个目录存储的是你整个项目的全局视图:

1
2
3
4
5
6
7
8
9
10
11
<!-- app/Resources/views/base.html.php -->
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?php $view['slots']->output('title', 'Hello Application') ?></title>
    </head>
    <body>
        <?php $view['slots']->output('_content') ?>
    </body>
</html>

这两个布局中, $view['slots']->output('_content') 语句会被子模版index.html.phplayout.html.php 各自输出的内容所替换(下文中有更多关于 slot 的信息)。

正如你看到的,symfony提供了一个神秘的$view对象和对象的方法。在模板中,$view变量总是可以得到的并且引用一个专门的对象,这个对象提供了一堆方法来对模板引擎进行标记。

使用Slots 

一个slot(位置)是一个代码片段,定义在模板中,并且可重用在任何布局来装饰模板。在index.html.php模板中定义了title slot(位置):

1
2
3
4
5
6
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>
 
<?php $view['slots']->set('title', 'Hello World Application') ?>
 
Hello <?php echo $name ?>!

基本布局(base.html.php)中的头部已经有代码输出了标题(title):

1
2
3
4
5
<!-- app/Resources/views/base.html.php -->
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title><?php $view['slots']->output('title', 'Hello Application') ?></title>
</head>

output() 方法用来插入一个 slot(位置) 的内容,如果 slot(位置) 未定义,则可以选择采用默认值。_content只是一个特殊的slot(位置),它是用来包含渲染后的子模板。

关于大型的slots(位置),这里有一个扩展语法:

1
2
3
<?php $view['slots']->start('title') ?>
    Some large amount of HTML
<?php $view['slots']->stop() ?>

包含其他模板 

分享一段模板代码的最好方法是定义一个模板,然后这个模板可以融入其他模板。

创建一个hello.html.php模板:

1
2
<!-- app/Resources/views/Hello/hello.html.php -->
Hello <?php echo $name ?>!

然后修改 index.html.php 模板来包含上面的模板:

1
2
3
4
<!-- app/Resources/views/Hello/index.html.php -->
<?php $view->extend('AppBundle::layout.html.php') ?>
 
<?php echo $view->render('AppBundle:Hello:hello.html.php', array('name' => $name)) ?>

这个render()方法会分析并返回包含的其他模板的内容(这个方法和控制器中使用的方法正是同一个)。

嵌入其他控制器 

如果你想要在模板嵌入另一个控制器的结果应该怎么做呢?当处理 Ajax 时,这是非常有用的,或在嵌入模板时,需要一些主模板中没有的变量。

如果你创建了一个fancy action,并想要包含到index.html.php模板中,只需要使用下面代码:

1
2
3
4
5
6
7
<!-- app/Resources/views/Hello/index.html.php -->
<?php echo $view['actions']->render(
    new \Symfony\Component\HttpKernel\Controller\ControllerReference('AppBundle:Hello:fancy', array(
        'name'  => $name,
        'color' => 'green',
    ))
) ?>

在这里,AppBundle:Hello:fancy字符串引用的是Hello控制器的fancy action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/AppBundle/Controller/HelloController.php
 
class HelloController extends Controller
{
    public function fancyAction($name, $color)
    {
        // create some object, based on the $color variable
        $object = ...;
 
        return $this->render('AppBundle:Hello:fancy.html.php', array(
            'name'   => $name,
            'object' => $object
        ));
    }
 
    // ...
}

但是这个$view['actions']数组元素是在哪里定义的呢?就像$view['slots']一样,它被称为模板助手,并在下文会告诉你更多关于这方面的信息。

使用模板助手 

Symfony模板系统可以通过助手(helpers)很容易的被扩展。助手(helpers)是在模板环境中提供实用功能的 PHP 对象。actionsslots都是symfony内置的助手。

创建页面之间的链接 

说到web应用,创建页面之间的链接是必须的。作为对模板中的 URLs 硬编码的替换,router助手知道如何基于路由配置去生成URL。这样的话,你所有的url可以很容易地通过改变配置来更新:

1
2
3
<a href="<?php echo $view['router']->path('hello', array('name' => 'Thomas')) ?>">
    Greet Thomas!
</a>

这个path()方法需要路由的名称和一个参数数组作为参数。路由名称是引用主键下的路由名称并且参数是路由模式中定义的占位符的值:

1
2
3
4
# src/AppBundle/Resources/config/routing.yml
hello: # The route name 路由名称
    path:     /hello/{name}
    defaults: { _controller: AppBundle:Hello:index }

使用Assets: Images, JavaScripts 和 Stylesheets 

互联网怎么能没有images, JavaScripts, 和 stylesheets呢?Symfony提供了assets标签可以很容易的处理它们:

1
2
3
<link href="<?php echo $view['assets']->getUrl('css/blog.css') ?>" rel="stylesheet" type="text/css" />
 
<img src="<?php echo $view['assets']->getUrl('images/logo.png') ?>" />

assets 助手的主要目的是让应用程序更便携。要感谢有这个助手,你才可以在你的Web根目录下移动应用程序根目录,而不改变模板中的任何代码。

分析模板 

使用stopwatch 助手,你可以对你的”模板部分“计时并将其显示在 WebProfilerBundle 的 timeline 上:

1
2
3
<?php $view['stopwatch']->start('foo') ?>
... things that get timed
<?php $view['stopwatch']->stop('foo') ?>

如果模板中你不知一次使用相同的名称,计时将被分组在时间线的同一行上。

输出转义 

当使用PHP模板时,当它们被显示给用户的时候,转义变量:

1
<?php echo $view->escape($var) ?>

默认情况下,该escape()方法假定变量输出在HTML环境内。第二个参数允许你更改环境。例如,在一个JavaScript脚本中输出东西,要使用js环境:

1
<?php echo $view->escape($var, 'js') ?>

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)