monolyth/improse

PHP5 MVC项目的视图和模板系统

0.7.4 2022-11-15 16:22 UTC

This package is auto-updated.

Last update: 2024-09-15 20:20:36 UTC


README

PHP5 MVC项目的视图和模板前端

令人惊讶的是,大多数MVC框架对控制器和视图的理解完全错误。如果你曾经使用过其中一个,你会认识以下模式

<?php

class SomeController extends BaseController
{
    public function actionIndex()
    {
        $this->data = new SomeModel;
        $this->render('path/to/template.php', ['data' => $this->data]);
    }
}

这有多个原因,所有原因都源于正式的MVC理论

  1. 视图(在上面的例子中实际上是模板,这是另一回事)负责自己的数据。这里不是这样;控制器正在实例化一个模型并将其传递给模板/视图。
  2. 模板正在充当视图,这是错误的(它们是不同的概念)。
  3. 现在SomeController::indexActionSomeModel之间有紧密的耦合,这只有在动作更改某些内容时才是相关的(通常是一个POST处理程序)。

Improse是一个简单的视图层,用于纠正这些错误。

安装

Composer(推荐)

$ cd /path/to/project
$ composer require monomelodies/improse

手册

  1. 下载或克隆存储库;
  2. 在PSR-4自动加载器中将/path/to/improse/src添加到命名空间Improse

基本用法

<?php

use Improse\View;
$view = new View('/path/to/template/file');
echo $view->render();

基本视图定义了一个render方法,用于渲染请求的文件。因此,您也可以将渲染的视图传递给某些其他处理程序(例如,一个发射器)

<?php

return emit($view->render());

视图还有一个__toString方法,它简单地返回render的结果,因此以下两个实际上是等效的

<?php

echo $view->render();
echo $view;

主要区别在于,由于__toString在PHP中不能抛出异常,它捕获它们并使用filp\whoops来显示它们,而render则直接抛出。

定义视图数据

所有公共成员都被render方法视为“视图数据”

<?php

$view->foo = 'bar';
echo $view; // The template now has $foo with value "bar"

在现实世界中,您的视图将需要收集数据以进行渲染。因此,您将主要使用View类作为基类进行扩展

<?php

class MyView extends Improse\View
{
    // Either define the template on the class...
    protected $template = '/path/to/template';

    public function __construct(PDO $db)
    {
        // ...or pass it to the parent constructor.
        parent::__construct('/path/to/template');
        $stmt = $db->prepare('SELECT foo FROM bar');
        $stmt->execute();
        $this->foo = $stmt->fetch(PDO::FETCH_ASSOC);
    }
}

使模板有用

Improse视图的基本行为是include模板文件。因此,您可以传递HTML(或其他静态格式)给它,或者一个PHP文件

<?php

$view = new View('/path/to/my/file.php');
$view->foo = 'bar';
echo $view;
<html>
    <body><?=$foo?></body>
</html>

嵌套视图

视图也可以包含其他视图

<?php

$template = new View('/path/to/main/template');
$view = new View('/path/to/some/page');
$template->page = $view;
echo $view;

模板现在可以简单地在某处echo $page

大多数模板引擎(见下文)也支持此功能,但在您在应用程序的不同部分混合多个模板系统时,例如TwigBladeMoustache,这可能会非常有用。在这一点上,Improse是“模板系统中立”的。

将模板绑定到视图

在除了最简单以外的任何应用程序中,视图都将包含逻辑,因此您会发现自己扩展基本视图以创建自定义的每页或每部分视图

<?php

class MyView extends Improse\View
{
    // Define the template on the class...
    protected $template = '/path/to/template';

    public function __construct($template = null)
    {
        parent::__construct($template);
        // [...snip all logic for MyView...]
    }
}

请注意,您仍然可以按情况覆盖模板,因为我们正在调用带有参数的父__construct或带有参数的父类构造函数

<?php

$view = new MyView('/some/custom/template');

即时逻辑

您还可以选择将所有逻辑放在覆盖的render方法中。完成时,将parent::render()转发并返回其结果

<?php

class MyView extends Improse\View
{
    protected $template = '/path/to/template';

    public function render()
    {
        // [...snip all logic for MyView...]
        return parent::render();
    }
}

这种策略允许您预先定义所有视图,但只有在实际渲染时才进行繁重的操作(例如,从RMDBS检索数据)(基于您的应用程序逻辑)。

模板引擎

尽管PHP本身就是一种模板引擎,但许多人(包括我们)更喜欢使用单独的模板引擎,例如TwigSmarty

集成模板引擎只需在扩展类中重写render方法,并让它使用自己的逻辑即可。使用Twig的示例可能如下所示

<?php

class TwigView extends Improse\View
{
    // ...[snip custom logic]...

    public function render()
    {
        $loader = new Twig_Loader_Filesystem('/path/to/templates');
        $twig = new Twig_Environment($loader, [
            'cache' => '/path/to/cache/dir',
            'auto_reload' => true, // or false
            'debug' => true, // or false
        ]);
        return $this->twig->render($this->template, $this->getVariables());
    }
}

默认的render实现不做任何事情,因此你的重写只需要处理实际的输出(虽然当然你也可以让它做更多,比如记录日志等)。

错误处理

Improse使用filp\whoops来美化打印由__toString调用引发的异常错误(因为在PHP中它们不能抛出异常)。

由于“whoops”可能在任何子视图中触发,如果你真的很在意你的错误HTML是否有效(例如,如果它们已经输出,可能会包含重复的<head>标签),你应该在应用程序逻辑中处理这个问题(例如使用输出缓冲)。Improse视图提供了一个静态的whoops方法,如果发生任何错误则返回true

<?php

if (Improse\View::whoops()) {
    // error...
} else {
    // ok, display page
}

当然,既然你无论如何都想修复错误,你不妨将其输出到屏幕上。但根据你的应用程序是处于开发模式还是生产模式,你也可以表现得更加友好。视图还有一个静态的$swallowErrors属性。它默认为false,但将其设置为任何非false值,有错误的视图将渲染该值(因此理想情况下你应该将其设置为字符串消息,如"Error! We're flogging the programmer!")。

这些都是如此基础...

所以你已经阅读了上面的内容,并查看了Improse的源代码(诚然非常小)。也许你自己在想,你为什么要使用Improse;毕竟它只是几行代码。

但是,它可以节省一些样板代码,并通过扩展基本视图,模板、变量和渲染逻辑已经就位。它还强迫你以正确的MVC方式实现视图: