[TOC]
# 使用视图
视图表示应用程序的用户界面。视图通常是带有嵌入式PHP代码的HTML文件,这些代码执行仅与数据表示相关的任务。视图处理向Web浏览器或用于从您的应用程序发出请求的其他工具提供数据的工作。
`Phalcon\Mvc\View` 和 `Phalcon\Mvc\View\Simple` 负责管理MVC应用程序的视图层。
## 将视图与控制器集成
一旦特定控制器完成其循环,Phalcon就会自动将执行传递给视图组件。视图组件将在views文件夹中查找名称与执行的最后一个控制器的名称相同的文件夹,然后查找名为最后执行的操作的文件。例如,如果请求URL *http://127.0.0.1/blog/posts/show/301*,Phalcon将按如下方式解析URL:
| 服务地址 | 127.0.0.1 |
| ----------------- | --------- |
| Phalcon 目录 | blog |
| Controller | posts |
| Action | show |
| Parameter | 301 |
调度程序将查找`PostsController`及其动作`showAction`。此示例的简单控制器文件:
```php
view->postId = $postId;
}
}
```
`setVar()` 方法允许我们按需创建视图变量,以便可以在视图模板中使用它们。上面的示例演示了如何将`$postId`参数传递给相应的视图模板。
## 分层渲染
`Phalcon\Mvc\View` 支持文件层次结构,是Phalcon中视图呈现的默认组件。此层次结构允许公共布局点(常用视图),以及定义相应视图模板的控制器命名文件夹。
默认情况下,此组件使用PHP本身作为模板引擎,因此视图应具有`.phtml`扩展名。如果views目录是*app/views*,则视图组件将自动查找这3个视图文件。
| 名称 | 文件 | 描述 |
| ----------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 动作视图 | app/views/posts/show.phtml | 这是与操作相关的视图。只有在执行`show`动作时才会显示它。 |
| 控制器布局 | app/views/layouts/posts.phtml | 这是与控制器相关的视图。它仅针对控制器“posts”内执行的每个动作显示。布局中实现的所有代码都将重用于此控制器中的所有操作。 |
| 主布局 | app/views/index.phtml | 这是针对应用程序中执行的每个控制器或操作显示的主要操作。 |
您不需要实现上面提到的所有文件。`Phalcon\Mvc\View`简单地移动到文件层次结构中的下一个视图级别。如果实现了所有三个视图文件,则将按如下方式处理它们:
```php
This is show view!
I have received the parameter
```
```php
This is the "posts" controller layout!
getContent(); ?>
```
```php
Example
This is main layout!
getContent(); ?>
```
注意调用方法`$this->getContent()`的行。此方法指示`Phalcon\Mvc\View`在哪里注入层次结构中执行的上一个视图的内容。对于上面的示例,输出将是:
.. figure:: ../_static/img/views-1.png :align: center
请求生成的HTML将是:
```php
Example
This is main layout!
This is the "posts" controller layout!
This is show view!
I have received the parameter 101
```
### 使用模板
模板是可用于共享公共视图代码的视图。它们充当控制器布局,因此您需要将它们放在layouts目录中。
模板可以在布局之前呈现(使用`$this->view->setTemplateBefore()`),也可以在布局之后渲染(`this->view->setTemplateAfter()`)。在以下示例中,模板(`layouts/common.phtml`)在控制器布局(`layouts/posts.phtml`)之后渲染:
```php
view->setTemplateAfter('common');
}
public function lastAction()
{
$this->flash->notice(
'These are the latest posts'
);
}
}
```
```php
Blog's title
getContent(); ?>
```
```php
getContent(); ?>
```
```php
Blog Title
getContent(); ?>
```
```php
This is a title
This is the post content
This is another title
This is another post content
```
最终输出如下:
```php
Blog's title
Blog Title
This is a title
This is the post content
This is another title
This is another post content
```
如果我们使用了`$this->view->setTemplateBefore('common')`,那么这将是最终输出:
```php
Blog's title
Blog Title
This is a title
This is the post content
This is another title
This is another post content
```
### 控制渲染级别
如上所示,`Phalcon\Mvc\View`支持视图层次结构。您可能需要控制视图组件生成的渲染级别。方法`Phalcon\Mvc\View::setRenderLevel()`提供此功能。
可以从控制器或从上级视图层调用此方法以干扰渲染过程。
```php
view->setRenderLevel(
View::LEVEL_NO_RENDER
);
// ...
}
public function showAction($postId)
{
// Shows only the view related to the action
$this->view->setRenderLevel(
View::LEVEL_ACTION_VIEW
);
}
}
```
可用的渲染级别为:
| 类常量 | 描述 | 顺序 |
| ----------------------- | ------------------------------------------------------------------------ |:-----:|
| `LEVEL_NO_RENDER` | 表示避免生成任何类型渲染。 | |
| `LEVEL_ACTION_VIEW` | 生成与操作关联的视图。 | 1 |
| `LEVEL_BEFORE_TEMPLATE` | 在控制器布局之前生成模板。 | 2 |
| `LEVEL_LAYOUT` | 生成渲染到控制器布局。 | 3 |
| `LEVEL_AFTER_TEMPLATE` | 在控制器布局之后生成到模板的渲染。 | 4 |
| `LEVEL_MAIN_LAYOUT` | 生成显示到主布局。文件`views/index.phtml` | 5 |
### 禁用渲染级别
您可以永久或暂时禁用渲染级别。如果在整个应用程序中根本不使用某个级别,则可以永久禁用该级别:
```php
set(
'view',
function () {
$view = new View();
// Disable several levels
$view->disableLevel(
[
View::LEVEL_LAYOUT => true,
View::LEVEL_MAIN_LAYOUT => true,
]
);
return $view;
},
true
);
```
或者在应用程序的某些部分暂时禁用:
```php
view->disableLevel(
View::LEVEL_MAIN_LAYOUT
);
}
}
```
### 选择视图
如上所述,当 `Phalcon\Mvc\View` 由`Phalcon\Mvc\Application`管理时,渲染的视图是与最后一个控制器和执行的操作相关的视图。您可以使用 `Phalcon\Mvc\View::pick()` 方法覆盖它:
```php
view->pick('products/search');
// Pick 'views-dir/books/list' as view to render
$this->view->pick(
[
'books',
]
);
// Pick 'views-dir/products/search' as view to render
$this->view->pick(
[
1 => 'search',
]
);
}
}
```
### 禁用视图
如果您的控制器在视图中没有产生任何输出(或者甚至没有输出),您可以禁用视图组件以避免不必要的处理:
```php
view->disable();
}
}
```
或者,您可以返回`false`以产生相同的效果:
```php
response->redirect('index/index');
}
}
```
## 简单的渲染
`Phalcon\Mvc\View\Simple` 是 `Phalcon\Mvc\View`的替代组件。 它保留了`Phalcon\Mvc\View`的大部分哲学,但缺乏文件层次结构,事实上,它是对应文件的主要特征。
该组件允许开发人员控制何时呈现视图及其位置。此外,该组件可以利用模板引擎(如`Volt`等)中可用的视图继承。
必须在服务容器中替换默认组件:
```php
set(
'view',
function () {
$view = new SimpleView();
$view->setViewsDir('../app/views/');
return $view;
},
true
);
```
必须在`Phalcon\Mvc\Application`中禁用自动渲染(如果需要):
```php
useImplicitView(false);
$response = $application->handle();
$response->send();
} catch (Exception $e) {
echo $e->getMessage();
}
```
要渲染视图,必须显式调用`render`方法,指示要显示的视图的相对路径:
```php
view->render('index');
// Render 'views-dir/posts/show.phtml'
echo $this->view->render('posts/show');
// Render 'views-dir/index.phtml' passing variables
echo $this->view->render(
'index',
[
'posts' => Posts::find(),
]
);
// Render 'views-dir/posts/show.phtml' passing variables
echo $this->view->render(
'posts/show',
[
'posts' => Posts::find(),
]
);
}
}
```
这与`Phalcon\Mvc\View` 不同,它的`render()` 方法使用控制器和动作作为参数:
```php
Posts::find(),
];
// Phalcon\Mvc\View
$view = new \Phalcon\Mvc\View();
echo $view->render('posts', 'show', $params);
// Phalcon\Mvc\View\Simple
$simpleView = new \Phalcon\Mvc\View\Simple();
echo $simpleView->render('posts/show', $params);
```
## 使用Partials
部分模板是将渲染过程分解为更简单,更易于管理的块的另一种方法,可以由应用程序的不同部分重用。使用partial,您可以移动代码以将特定的响应片段呈现给自己的文件。
使用partials的一种方法是将它们视为子例程的等价物:作为一种将细节移出视图的方法,以便更容易理解代码。例如,您可能有一个如下所示的视图:
```php
partial('shared/ad_banner'); ?>
Robots
Check out our specials for robots:
...
```
`partial()` 方法接受第二个参数作为变量/参数数组,它们只存在于partial的范围内:
```php
partial('shared/ad_banner', ['id' => $site->id, 'size' => 'big']); ?>
```
## 将值从控制器传输到视图
`Phalcon\Mvc\View`在每个控制器中都可以使用视图变量(`$this->view`)。您可以使用该对象通过使用`setVar()` 方法从控制器操作直接将变量设置到视图。
```php
getPosts();
// Pass all the username and the posts to the views
$this->view->setVar('username', $user->username);
$this->view->setVar('posts', $posts);
// Using the magic setter
$this->view->username = $user->username;
$this->view->posts = $posts;
// Passing more than one variable at the same time
$this->view->setVars(
[
'username' => $user->username,
'posts' => $posts,
]
);
}
}
```
将在视图中创建具有 `setVar()` 的第一个参数名称的变量,以供使用。变量可以是任何类型,从简单的字符串,整数等变量到更复杂的结构,如数组,集合等。
```php
{{ username }}'s Posts
', $post->title, '';
}
?>
```
## 缓存视图片段
有时,当您开发动态网站并且其中某些区域不经常更新时,请求之间的输出完全相同。`Phalcon\Mvc\View` 提供缓存部分或整个渲染输出以提高性能。
`Phalcon\Mvc\View` 与 `Phalcon\Cache` 集成,提供了一种缓存输出片段的简便方法。您可以手动设置缓存处理程序或设置全局处理程序:
```php
view->cache(true);
}
public function showArticleAction()
{
// Cache this view for 1 hour
$this->view->cache(
[
'lifetime' => 3600,
]
);
}
public function resumeAction()
{
// Cache this view for 1 day with the key 'resume-cache'
$this->view->cache(
[
'lifetime' => 86400,
'key' => 'resume-cache',
]
);
}
public function downloadAction()
{
// Passing a custom service
$this->view->cache(
[
'service' => 'myCache',
'lifetime' => 86400,
'key' => 'resume-cache',
]
);
}
}
```
当我们没有定义缓存的key时,组件使用控制器名称的[MD5](http://php.net/manual/en/function.md5.php)哈希和当前以`controller/view`格式呈现的视图自动创建一个键。为每个操作定义一个键是一个好习惯,这样您就可以轻松识别与每个视图关联的缓存。
当View组件需要缓存某些内容时,它将从服务容器中请求缓存服务。此服务的服务名称约定是`viewCache`:
```php
set(
'viewCache',
function () {
// Cache data for one day by default
$frontCache = new OutputFrontend(
[
'lifetime' => 86400,
]
);
// Memcached connection settings
$cache = new MemcacheBackend(
$frontCache,
[
'host' => 'localhost',
'port' => '11211',
]
);
return $cache;
}
);
```
>[warning] 前端必须始终为`Phalcon\Cache\Frontend\Output`,并且服务viewCache必须在服务容器(DI)中注册为始终打开(不共享)。
使用视图时,可以使用缓存来防止控制器需要在每个请求上生成视图数据。
为此,我们必须使用密钥唯一地标识每个缓存。首先,我们验证缓存不存在或已过期,以使计算/查询在视图中显示数据:
```php
view->getCache()->exists('downloads')) {
// Query the latest downloads
$latest = Downloads::find(
[
'order' => 'created_at DESC',
]
);
$this->view->latest = $latest;
}
// Enable the cache with the same key 'downloads'
$this->view->cache(
[
'key' => 'downloads',
]
);
}
}
```
[PHP替代站点](https://github.com/phalcon/php-site)是实现片段缓存的示例。
## 模板引擎
模板引擎可帮助设计人员在不使用复杂语法的情况下创建视图。Phalcon包括一个名为`Volt`的强大而快速的模板引擎。`Phalcon\Mvc\View`许您使用其他模板引擎而不是普通的PHP或Volt。
使用不同的模板引擎通常需要使用外部PHP库进行复杂的文本解析,以便为用户生成最终输出。这通常会增加应用程序将使用的资源数量。
如果使用外部模板引擎,`Phalcon\Mvc\View`提供完全相同的视图层次结构,并且仍然可以更加努力地访问这些模板中的API。
该组件使用适配器,这些帮助Phalcon以统一的方式与这些外部模板引擎对话,让我们看看如何进行这种集成。
### 创建自己的模板引擎适配器
有许多模板引擎,您可能希望集成或创建自己的模板引擎。开始使用外部模板引擎的第一步是为它创建一个适配器。
模板引擎适配器是一个类,它充当`Phalcon\Mvc\View`和模板引擎本身之间的桥梁。通常它只需要实现两个方法:`__construct()` 和`render()`。第一个接收`Phalcon\Mvc\View`实例,该实例创建引擎适配器和应用程序使用的DI容器。
方法 `render()` 接受视图文件的绝对路径和使用`$this->view->setVar()`设置的视图参数。您可以在必要时阅读或要求它。
```php
_view;
// Access options
$options = $this->_options;
// Render the view
// ...
}
}
```
### 更改模板引擎
您可以完全替换模板引擎,也可以同时使用多个模板引擎。方法 `Phalcon\Mvc\View::registerEngines()` 接受包含定义模板引擎的数据的数组。每个引擎的关键是一个扩展,有助于区分彼此。与特定引擎相关的模板文件必须具有这些扩展名。
使用`Phalcon\Mvc\View::registerEngines()`定义模板引擎的顺序定义了执行的相关性。如果`Phalcon\Mvc\View`找到两个具有相同名称但扩展名不同的视图,则它只会呈现第一个视图。
如果要为应用程序中的每个请求注册模板引擎或其中一组。您可以在创建视图服务时注册它:
```php
set(
'view',
function () {
$view = new View();
// A trailing directory separator is required
$view->setViewsDir('../app/views/');
// Set the engine
$view->registerEngines(
[
'.my-html' => 'MyTemplateAdapter',
]
);
// Using more than one template engine
$view->registerEngines(
[
'.my-html' => 'MyTemplateAdapter',
'.phtml' => 'Phalcon\Mvc\View\Engine\Php',
]
);
return $view;
},
true
);
```
[Phalcon Incubator](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/View/Engine)上有几个模板引擎可用的适配器
## 在View中注入服务
每个执行的视图都包含在`Phalcon\Di\Injectable` 实例中,可以轻松访问应用程序的服务容器。
以下示例说明如何使用具有框架约定的URL编写jQuery [ajax请求](http://api.jquery.com/jQuery.ajax/)。通过访问具有相同名称的属性,在视图中注入服务`url`(通常是`Phalcon\Mvc\Url`):
```js
```
## 独立组件
Phalcon中的所有组件都可以单独用作*胶水*组件,因为它们彼此松散耦合:
### 分层渲染
在独立模式下使用`Phalcon\Mvc\View`可以在下面演示:
```php
setViewsDir('../app/views/');
// Passing variables to the views, these will be created as local variables
$view->setVar('someProducts', $products);
$view->setVar('someFeatureEnabled', true);
// Start the output buffering
$view->start();
// Render all the view hierarchy related to the view products/list.phtml
$view->render('products', 'list');
// Finish the output buffering
$view->finish();
echo $view->getContent();
```
还提供了一个简短的语法:
```php
getRender(
'products',
'list',
[
'someProducts' => $products,
'someFeatureEnabled' => true,
],
function ($view) {
// Set any extra options here
$view->setViewsDir('../app/views/');
$view->setRenderLevel(
View::LEVEL_LAYOUT
);
}
);
```
### 简单的渲染
在独立模式下使用`Phalcon\Mvc\View\Simple`可以在下面演示:
```php
setViewsDir('../app/views/');
// Render a view and return its contents as a string
echo $view->render('templates/welcomeMail');
// Render a view passing parameters
echo $view->render(
'templates/welcomeMail',
[
'email' => $email,
'content' => $content,
]
);
```
## 视图事件
`Phalcon\Mvc\View` 和 `Phalcon\Mvc\View\Simple` 能够将事件发送到`EventsManager`(如果存在)。
使用类型`view`触发事件。返回布尔值false时的某些事件可能会停止活动操作。支持以下事件:
| 事件名称 | 触发 | Can stop operation? |
| ---------------- | --------------------------------------------- |:-------------------:|
| beforeRender | 在开始渲染过程之前触发 | Yes |
| beforeRenderView | 在渲染现有视图之前触发 | Yes |
| afterRenderView | 在渲染现有视图之后触发 | No |
| afterRender | 完成渲染过程后触发 | No |
| notFoundView | 未找到视图时触发 | No |
以下示例演示如何将侦听器附加到此组件:
```php
set(
'view',
function () {
// Create an events manager
$eventsManager = new EventsManager();
// Attach a listener for type 'view'
$eventsManager->attach(
'view',
function (Event $event, $view) {
echo $event->getType(), ' - ', $view->getActiveRenderPath(), PHP_EOL;
}
);
$view = new View();
$view->setViewsDir('../app/views/');
// Bind the eventsManager to the view component
$view->setEventsManager($eventsManager);
return $view;
},
true
);
```
以下示例显示如何使用[Tidy](http://www.php.net/manual/en/book.tidy.php)创建一个清理/修复渲染过程生成的HTML的插件:
```php
true,
'output-xhtml' => true,
'show-body-only' => true,
'wrap' => 0,
];
$tidy = tidy_parse_string(
$view->getContent(),
$tidyConfig,
'UTF8'
);
$tidy->cleanRepair();
$view->setContent(
(string) $tidy
);
}
}
// Attach the plugin as a listener
$eventsManager->attach(
'view:afterRender',
new TidyPlugin()
);
```
';