monolyth / improse
PHP5 MVC项目的视图和模板系统
Requires
- filp/whoops: ^1.1
Requires (Dev)
- gentry/gentry: ^0.10.0
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理论
- 视图(在上面的例子中实际上是模板,这是另一回事)负责自己的数据。这里不是这样;控制器正在实例化一个模型并将其传递给模板/视图。
- 模板正在充当视图,这是错误的(它们是不同的概念)。
- 现在
SomeController::indexAction
和SomeModel
之间有紧密的耦合,这只有在动作更改某些内容时才是相关的(通常是一个POST
处理程序)。
Improse是一个简单的视图层,用于纠正这些错误。
安装
Composer(推荐)
$ cd /path/to/project
$ composer require monomelodies/improse
手册
- 下载或克隆存储库;
- 在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
。
大多数模板引擎(见下文)也支持此功能,但在您在应用程序的不同部分混合多个模板系统时,例如
Twig
、Blade
和Moustache
,这可能会非常有用。在这一点上,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本身就是一种模板引擎,但许多人(包括我们)更喜欢使用单独的模板引擎,例如Twig或Smarty。
集成模板引擎只需在扩展类中重写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方式实现视图: