darkstar/view

提供 TemplateView 和 TwoStepView 模式的实现,支持使用 PHP 作为模板语言,同时支持助手和闭包作为模板。

v1.0.0 2020-11-13 07:21 UTC

This package is not auto-updated.

Last update: 2024-09-29 02:27:36 UTC


README

这是从 Aura View 分支出来的,已修改为支持 php 7.x

Aura View

此包使用 PHP 本身作为模板语言实现了 TemplateViewTwoStepView 模式。它支持基于文件和基于闭包的模板,以及助手和部分。

它继承了如 SavantZend_ViewSolar_View 等系统。

前言

安装

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

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

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

质量

Scrutinizer Code Quality Code Coverage Build Status

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

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

社区

要提问、提供反馈或以其他方式与 Aura 社区沟通,请加入我们的 Google Group,关注 @auraphp 或在 Freenode 上与我们一起聊天。

入门

实例化

要实例化一个 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

注册视图模板

现在我们已经有了 View,我们需要将其添加到其视图模板注册表中命名的模板。这些通常是 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

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

设置数据

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

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

注意:请记住,模板逻辑中的$this引用的是View对象,因此分配给View的数据可以像属性一样访问$this

setData()方法将覆盖View对象中所有现有的数据。另一方面,addData()方法将合并到View对象中现有的数据。

调用一步式视图

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

<?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实例化View来管理助手。我们可以通过HelperRegistry注册闭包或其他可调用对象作为助手。然后我们可以像调用View上的方法一样调用这些助手。

<?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没有指定特定的类用于其助手管理器。这意味着您可以在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追加到模板名称的末尾。

更改模板文件扩展名

默认情况下,每个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',
    ],
);
?>

如果您传递了视图和布局的映射信息或路径,您不需要调用 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');