aura/view

提供了模板视图(TemplateView)和双步视图(TwoStepView)模式的实现,支持使用PHP作为模板语言,并支持辅助函数和闭包作为模板。

2.4.0 2022-02-05 09:35 UTC

This package is auto-updated.

Last update: 2024-09-05 15:24:49 UTC


README

本软件包提供了使用PHP自身作为模板语言的模板视图(TemplateView)和双步视图(TwoStepView)模式的实现。它支持基于文件和基于闭包的模板,以及辅助函数和部分。

它继承了如 SavantZend_ViewSolar_View 等系统。

前言

安装

此库需要PHP 5.4或更高版本;我们建议使用最新的PHP版本。它没有用户依赖。

可以通过Composer以 aura/view 的方式安装和自动加载。

或者,下载一个版本 或克隆此存储库,然后要求或包含其 autoload.php 文件。

质量

Scrutinizer Code Quality codecov Continuous Integration

要在命令行中运行单元测试,请发出 composer install 命令,然后在包根目录下发出 ./vendor/bin/phpunit 命令。这需要 Composer 作为 composer 可用。

此库试图遵守 PSR-1PSR-2PSR-4。如果您注意到遵守方面的疏忽,请通过拉取请求发送补丁。

社区

要提问、提供反馈或与其他Aura社区成员交流,请加入我们的 Google Group,关注 @auraphp on Twitter,或在Freenode上的#auraphp聊天室与我们聊天。

入门指南

实例化

要实例化一个 View 对象,请使用 ViewFactory

<?php
$view_factory = new \Aura\View\ViewFactory;
$view = $view_factory->newInstance();
?>

输出转义

注重安全性的观察者会注意到本文件中的所有示例都使用了手动转义输出。由于此包并非针对任何特定媒体类型,因此它 提供转义功能。

当您通过模板生成输出时,您 必须 根据安全性要求适当地转义它。这意味着HTML模板应使用HTML转义,CSS模板应使用CSS转义,XML模板应使用XML转义,PDF模板应使用PDF转义,RTF模板应使用RTF转义,等等。

有关一组良好的HTML转义程序,请考虑 Aura.Html

注册视图模板

现在我们有了视图,需要将其视图模板注册表中添加命名模板。这些通常是PHP文件路径,但模板也可以是闭包。例如

<?php
$view_registry = $view->getViewRegistry();
$view_registry->set('browse', '/path/to/views/browse.php');
?>

browse.php文件可能看起来像这样

<?php
foreach ($this->items as $item) {
    $id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8');
    $name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8');
    echo "Item ID #{$id} is '{$name}'." . PHP_EOL;
}
?>

注意我们在模板中使用echo,而不是return

注意:模板逻辑将在视图对象的作用域内执行,这意味着模板代码中的$this将引用视图对象。基于闭包的模板也是如此。

设置数据

我们几乎总是希望在模板中使用动态数据。要将数据集合分配给视图,请使用setData()方法,并传入一个数组或对象。然后我们可以像使用视图对象的属性一样使用数据元素。

<?php
$view->setData(array(
    'items' => array(
        array(
            'id' => '1',
            'name' => 'Foo',
        ),
        array(
            'id' => '2',
            'name' => 'Bar',
        ),
        array(
            'id' => '3',
            'name' => 'Baz',
        ),
    )
));
?>

注意:回想一下,模板逻辑中的$this引用的是视图对象,因此分配给视图的数据可以作为$this的属性访问。

setData()方法将覆盖视图对象中所有现有的数据。addData()方法则与其他视图对象中的现有数据合并。

调用一步视图

现在我们已经注册了模板并分配了一些数据给视图,我们告诉视图使用哪个模板,然后调用视图

<?php
$view->setView('browse');
$output = $view->__invoke(); // or just $view()
?>

在这种情况下,$output将类似于以下内容

Item #1 is 'Foo'.
Item #2 is 'Bar'.
Item #3 is 'Baz'.

使用子模板(也称为“部分”)

有时我们可能希望将模板分成多个部分。我们可以使用主模板代码中的render()方法来渲染这些“部分”模板块。

首先,我们将子模板放入视图注册表(或者如果它在布局中使用,则放入布局注册表)。然后我们从主模板代码中渲染它。子模板可以使用我们喜欢的任何命名方案。一些系统使用在部分模板前加下划线的约定,以下示例将使用该约定。

其次,我们可以传递一个数组,其中包含要提取到部分模板局部作用域中的变量。($this变量始终可用。)

例如,让我们将我们的browse.php模板文件拆分,以便它使用一个用于显示项目的子模板。

<?php
// add templates to the view registry
$view_registry = $view->getViewRegistry();

// the "main" template
$view_registry->set('browse', '/path/to/views/browse.php');

// the "sub" template
$view_registry->set('_item', '/path/to/views/_item.php');
?>

我们将browse.php中的项目显示代码提取到_item.php

<?php
$id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8');
$name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8')
echo "Item ID #{$id} is '{$name}'." . PHP_EOL;
?>

然后我们修改browse.php以使用子模板

<?php
foreach ($this->items as $item) {
    echo $this->render('_item', array(
        'item' => $item,
    ));
?>

输出将与我们在调用视图时得到的输出相同。

注意:作为替代,我们也可以使用includerequire直接在当前模板作用域中执行PHP文件。

使用部分

部分类似于子模板(也称为“部分”),除了它们是内联捕获以供以后使用。通常,它们由视图模板用于捕获布局模板的输出。

例如,我们可以在视图模板中捕获输出到一个命名的部分...

<?php
// begin buffering output for a named section
$this->beginSection('local-nav');

echo "<div>";
// ... echo the local navigation output ...
echo "</div>";

// end buffering and capture the output
$this->endSection();
?>

...然后在布局模板中使用该输出

<?php
if ($this->hasSection('local-nav')) {
    echo $this->getSection('local-nav');
} else {
    echo "<div>No local navigation.</div>";
}
?>

此外,可以使用setSection()方法直接设置部分内容,而不是捕获它

<?php
$this->setSection('local-nav', $this->render('_local-nav'));
?>

使用助手

ViewFactory使用空的HelperRegistry来管理助手实例化视图。我们可以通过HelperRegistry注册闭包或其他可调用对象作为助手。然后我们可以像调用视图的方法一样调用这些助手。

<?php
$helpers = $view->getHelpers();
$helpers->set('hello', function ($name) {
    return "Hello {$name}!";
});

$view_registry = $view->getViewRegistry();
$view_registry->set('index', function () {
    echo $this->hello('World');
});

$view->setView('index');
$output = $view();
?>

此库不附带任何视图助手。您需要将您自己的助手作为闭包或可调用对象添加到注册表中。

自定义助手管理器

视图的辅助管理器没有指定特定的类类型。这意味着您可以在View构造时注入任意对象来管理辅助器。要做到这一点,请将您自己的辅助器管理器传递给ViewFactory

<?php
class OtherHelperManager
{
    public function __call($helper_name, $args)
    {
        // logic to call $helper_name with
        // $args and return the result
    }
}

$helpers = new OtherHelperManager;
$view = $view_factory->newInstance($helpers);
?>

对于一组全面的HTML辅助器,包括表单和输入辅助器,请考虑使用Aura.Html包及其HelperLocator,作为本包中HelperRegistry的替代方案。您可以通过以下方式将其传递给ViewFactory

<?php
$helpers_factory = new Aura\Html\HelperLocatorFactory;
$helpers = $helpers_factory->newInstance();
$view = $view_factory->newInstance($helpers);
?>

渲染两步视图

为了将主要内容包装在布局中,作为两步视图的一部分,我们将布局模板注册到View中,然后调用setLayout()来选择其中一个用于第二步。(如果没有设置布局,则不会执行第二步。)

假设我们已经在视图注册表中设置了上面的browse模板。然后我们将名为default的布局模板设置到布局注册表中

<?php
$layout_registry = $view->getLayoutRegistry();
$layout_registry->set('default', '/path/to/layouts/default.php');
?>

default.php布局模板可能看起来像这样

<html>
<head>
    <title>My Site</title>
</head>
<body>
<?= $this->getContent(); ?>
</body>
</html>

然后我们可以在View对象上设置视图和布局模板,然后调用它

<?php
$view->setView('browse');
$view->setLayout('default');
$output = $view->__invoke(); // or just $view()
?>

内部视图模板的输出将自动保留,并通过View对象的getContent()方法可用。布局模板随后调用getContent(),将内部视图结果放置在外部布局模板中。

注意。我们还可以在视图模板内部调用setLayout(),这样我们就可以在视图逻辑中挑选布局。

视图模板和布局模板都在同一个View对象内部执行。这意味着

  • 所有数据值在视图和布局之间共享。分配给视图或由视图修改的任何数据都按原样由布局使用。

  • 所有辅助器在视图和布局之间共享。这种共享情况允许视图在布局执行之前修改数据和辅助器。

  • 所有部分体在视图和布局之间共享。因此,从视图模板捕获的部分可以由布局模板使用。

闭包作为模板

视图和布局注册表接受闭包作为模板。例如,以下是基于闭包的上述browse.php_item.php模板文件的等效文件

<?php
$view_registry->set('browse', function () {
    foreach ($this->items as $item) {
        echo $this->render('_item', array(
            'item' => $item,
        ));
    }
);

$view_registry->set('_item', function (array $vars) {
    extract($vars, EXTR_SKIP);
    $id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8');
    $name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8')
    echo "Item ID #{$id} is '{$name}'." . PHP_EOL;
);
?>

注册基于闭包的模板时,继续使用echo而不是return来生成输出。闭包将被重新绑定到View对象,因此闭包中的$this将指向View,就像在基于文件的模板中一样。

对于基于闭包的子模板(也称为“部分”),需要做一点额外的工作。与基于文件的模板自动将传递的变量数组提取到局部作用域不同,基于闭包的模板必须

  1. 定义一个函数参数来接收注入的变量(在_item模板中的$vars参数);并且

  2. 使用extract()提取注入的变量。或者,闭包可以直接使用注入的变量参数。

除此之外,基于闭包的模板与基于文件的模板的工作方式完全相同。

注册模板搜索路径

我们还可以告诉视图和布局注册表在文件系统中搜索模板。首先,我们告诉注册表包含模板文件的目录

<?php
$view_registry = $view->getViewRegistry();
$view_registry->setPaths(array(
    '/path/to/foo',
    '/path/to/bar',
    '/path/to/baz'
));
?>

当我们稍后引用命名模板时,注册表将按从第一个目录到最后一个目录的顺序搜索。为了对搜索路径有更精细的控制,我们可以调用prependPath()来在搜索早期添加目录,或者调用appendPath()来在搜索后期添加目录。无论如何,View会在搜索目录时自动将.php添加到模板名称的末尾。

模板命名空间

我们还可以添加命名空间模板,我们可以使用namespace::template语法来引用。我们可以添加与命名空间对应的目录

<?php
$view_registry = $view->getViewRegistry();
$view_registry->appendPath('/path/to/templates', 'my-namespace');

$view->setView('my-namespace::browse');

当我们提到命名空间模板时,只有与该命名空间关联的路径将被搜索。

更改模板文件扩展名

默认情况下,每个 TemplateRegistry 会自动将 .php 追加到模板文件名。如果模板文件的扩展名不同,请使用 setTemplateFileExtension() 方法进行更改

<?php
$view_registry = $view->getViewRegistry();
$view_registry->setTemplateFileExtension('.phtml');
?>

用于布局的 TemplateRegistry 实例与用于视图的实例是分开的,因此可能还需要更改该实例的模板文件扩展名

<?php
$layout_registry = $view->getLayoutRegistry();
$layout_registry->setTemplateFileExtension('.phtml');
?>

高级配置

或者,您可以通过以下方式传递 $helpers、视图和布局的信息或路径。

<?php
$view_factory = new \Aura\View\ViewFactory;
$view = $view_factory->newInstance(
    $helpers = null,
    [
        'browse' => '/path/to/views/browse.php'
    ],
    [
        '/path/to/views/welcome',
        '/path/to/views/user',
    ],
    [
        'layout' => '/path/to/layouts/default.php'
    ],
    [
        '/path/to/layouts',
    ],
);
?>

如果您正在将映射信息或路径传递给 viewslayouts,则不需要调用 getViewRegistrygetLayoutRegistry 并设置映射信息。

例如

$view_registry = $view->getViewRegistry();
$view_registry->set('browse', '/path/to/views/browse.php');

$layout_registry = $view->getLayoutRegistry();
$layout_registry->set('default', '/path/to/layouts/default.php');