企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[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 <?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction($postId) { // Pass the $postId parameter to the view $this->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 <!-- app/views/posts/show.phtml --> <h3>This is show view!</h3> <p>I have received the parameter <?php echo $postId; ?></p> ``` ```php <!-- app/views/layouts/posts.phtml --> <h2>This is the "posts" controller layout!</h2> <?php echo $this->getContent(); ?> ``` ```php <!-- app/views/index.phtml --> <html> <head> <title>Example</title> </head> <body> <h1>This is main layout!</h1> <?php echo $this->getContent(); ?> </body> </html> ``` 注意调用方法`$this->getContent()`的行。此方法指示`Phalcon\Mvc\View`在哪里注入层次结构中执行的上一个视图的内容。对于上面的示例,输出将是: .. figure:: ../_static/img/views-1.png :align: center 请求生成的HTML将是: ```php <!-- app/views/index.phtml --> <html> <head> <title>Example</title> </head> <body> <h1>This is main layout!</h1> <!-- app/views/layouts/posts.phtml --> <h2>This is the "posts" controller layout!</h2> <!-- app/views/posts/show.phtml --> <h3>This is show view!</h3> <p>I have received the parameter 101</p> </body> </html> ``` ### 使用模板 模板是可用于共享公共视图代码的视图。它们充当控制器布局,因此您需要将它们放在layouts目录中。 模板可以在布局之前呈现(使用`$this->view->setTemplateBefore()`),也可以在布局之后渲染(`this->view->setTemplateAfter()`)。在以下示例中,模板(`layouts/common.phtml`)在控制器布局(`layouts/posts.phtml`)之后渲染: ```php <?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function initialize() { $this->view->setTemplateAfter('common'); } public function lastAction() { $this->flash->notice( 'These are the latest posts' ); } } ``` ```php <!-- app/views/index.phtml --> <!DOCTYPE html> <html> <head> <title>Blog's title</title> </head> <body> <?php echo $this->getContent(); ?> </body> </html> ``` ```php <!-- app/views/layouts/common.phtml --> <ul class='menu'> <li><a href='/'>Home</a></li> <li><a href='/articles'>Articles</a></li> <li><a href='/contact'>Contact us</a></li> </ul> <div class='content'><?php echo $this->getContent(); ?></div> ``` ```php <!-- app/views/layouts/posts.phtml --> <h1>Blog Title</h1> <?php echo $this->getContent(); ?> ``` ```php <!-- app/views/posts/last.phtml --> <article> <h2>This is a title</h2> <p>This is the post content</p> </article> <article> <h2>This is another title</h2> <p>This is another post content</p> </article> ``` 最终输出如下: ```php <!-- app/views/index.phtml --> <!DOCTYPE html> <html> <head> <title>Blog's title</title> </head> <body> <!-- app/views/layouts/common.phtml --> <ul class='menu'> <li><a href='/'>Home</a></li> <li><a href='/articles'>Articles</a></li> <li><a href='/contact'>Contact us</a></li> </ul> <div class='content'> <!-- app/views/layouts/posts.phtml --> <h1>Blog Title</h1> <!-- app/views/posts/last.phtml --> <article> <h2>This is a title</h2> <p>This is the post content</p> </article> <article> <h2>This is another title</h2> <p>This is another post content</p> </article> </div> </body> </html> ``` 如果我们使用了`$this->view->setTemplateBefore('common')`,那么这将是最终输出: ```php <!-- app/views/index.phtml --> <!DOCTYPE html> <html> <head> <title>Blog's title</title> </head> <body> <!-- app/views/layouts/posts.phtml --> <h1>Blog Title</h1> <!-- app/views/layouts/common.phtml --> <ul class='menu'> <li><a href='/'>Home</a></li> <li><a href='/articles'>Articles</a></li> <li><a href='/contact'>Contact us</a></li> </ul> <div class='content'> <!-- app/views/posts/last.phtml --> <article> <h2>This is a title</h2> <p>This is the post content</p> </article> <article> <h2>This is another title</h2> <p>This is another post content</p> </article> </div> </body> </html> ``` ### 控制渲染级别 如上所示,`Phalcon\Mvc\View`支持视图层次结构。您可能需要控制视图组件生成的渲染级别。方法`Phalcon\Mvc\View::setRenderLevel()`提供此功能。 可以从控制器或从上级视图层调用此方法以干扰渲染过程。 ```php <?php use Phalcon\Mvc\View; use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function findAction() { // This is an Ajax response so it doesn't generate any kind of view $this->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 <?php use Phalcon\Mvc\View; $di->set( 'view', function () { $view = new View(); // Disable several levels $view->disableLevel( [ View::LEVEL_LAYOUT => true, View::LEVEL_MAIN_LAYOUT => true, ] ); return $view; }, true ); ``` 或者在应用程序的某些部分暂时禁用: ```php <?php use Phalcon\Mvc\View; use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function findAction() { $this->view->disableLevel( View::LEVEL_MAIN_LAYOUT ); } } ``` ### 选择视图 如上所述,当 `Phalcon\Mvc\View` 由`Phalcon\Mvc\Application`管理时,渲染的视图是与最后一个控制器和执行的操作相关的视图。您可以使用 `Phalcon\Mvc\View::pick()` 方法覆盖它: ```php <?php use Phalcon\Mvc\Controller; class ProductsController extends Controller { public function listAction() { // Pick 'views-dir/products/search' as view to render $this->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 <?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function closeSessionAction() { // Close session // ... // Disable the view to avoid rendering $this->view->disable(); } } ``` 或者,您可以返回`false`以产生相同的效果: ```php <?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function closeSessionAction() { // ... // Disable the view to avoid rendering return false; } } ``` 您可以返回`response`对象以避免手动禁用视图: ```php <?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function closeSessionAction() { // Close session // ... // A HTTP Redirect return $this->response->redirect('index/index'); } } ``` ## 简单的渲染 `Phalcon\Mvc\View\Simple` 是 `Phalcon\Mvc\View`的替代组件。 它保留了`Phalcon\Mvc\View`的大部分哲学,但缺乏文件层次结构,事实上,它是对应文件的主要特征。 该组件允许开发人员控制何时呈现视图及其位置。此外,该组件可以利用模板引擎(如`Volt`等)中可用的视图继承。 必须在服务容器中替换默认组件: ```php <?php use Phalcon\Mvc\View\Simple as SimpleView; $di->set( 'view', function () { $view = new SimpleView(); $view->setViewsDir('../app/views/'); return $view; }, true ); ``` 必须在`Phalcon\Mvc\Application`中禁用自动渲染(如果需要): ```php <?php use Exception; use Phalcon\Mvc\Application; try { $application = new Application($di); $application->useImplicitView(false); $response = $application->handle(); $response->send(); } catch (Exception $e) { echo $e->getMessage(); } ``` 要渲染视图,必须显式调用`render`方法,指示要显示的视图的相对路径: ```php <?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { // Render 'views-dir/index.phtml' echo $this->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 <?php $params = [ 'posts' => 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 <div class='top'><?php $this->partial('shared/ad_banner'); ?></div> <div class='content'> <h1>Robots</h1> <p>Check out our specials for robots:</p> ... </div> <div class='footer'><?php $this->partial('shared/footer'); ?></div> ``` `partial()` 方法接受第二个参数作为变量/参数数组,它们只存在于partial的范围内: ```php <?php $this->partial('shared/ad_banner', ['id' => $site->id, 'size' => 'big']); ?> ``` ## 将值从控制器传输到视图 `Phalcon\Mvc\View`在每个控制器中都可以使用视图变量(`$this->view`)。您可以使用该对象通过使用`setVar()` 方法从控制器操作直接将变量设置到视图。 ```php <?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction() { $user = Users::findFirst(); $posts = $user->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 <h1> {{ username }}'s Posts </h1> <div class='post'> <?php foreach ($posts as $post) { echo '<h2>', $post->title, '</h2>'; } ?> </div> ``` ## 缓存视图片段 有时,当您开发动态网站并且其中某些区域不经常更新时,请求之间的输出完全相同。`Phalcon\Mvc\View` 提供缓存部分或整个渲染输出以提高性能。 `Phalcon\Mvc\View` 与 `Phalcon\Cache` 集成,提供了一种缓存输出片段的简便方法。您可以手动设置缓存处理程序或设置全局处理程序: ```php <?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function showAction() { // Cache the view using the default settings $this->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 <?php use Phalcon\Cache\Frontend\Output as OutputFrontend; use Phalcon\Cache\Backend\Memcache as MemcacheBackend; // Set the views cache service $di->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 <?php use Phalcon\Mvc\Controller; class DownloadController extends Controller { public function indexAction() { // Check whether the cache with key 'downloads' exists or has expired if ($this->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 <?php use Phalcon\DiInterface; use Phalcon\Mvc\Engine; class MyTemplateAdapter extends Engine { /** * Adapter constructor * * @param \Phalcon\Mvc\View $view * @param \Phalcon\Di $di */ public function __construct($view, DiInterface $di) { // Initialize here the adapter parent::__construct($view, $di); } /** * Renders a view using the template engine * * @param string $path * @param array $params */ public function render($path, $params) { // Access view $view = $this->_view; // Access options $options = $this->_options; // Render the view // ... } } ``` ### 更改模板引擎 您可以完全替换模板引擎,也可以同时使用多个模板引擎。方法 `Phalcon\Mvc\View::registerEngines()` 接受包含定义模板引擎的数据的数组。每个引擎的关键是一个扩展,有助于区分彼此。与特定引擎相关的模板文件必须具有这些扩展名。 使用`Phalcon\Mvc\View::registerEngines()`定义模板引擎的顺序定义了执行的相关性。如果`Phalcon\Mvc\View`找到两个具有相同名称但扩展名不同的视图,则它只会呈现第一个视图。 如果要为应用程序中的每个请求注册模板引擎或其中一组。您可以在创建视图服务时注册它: ```php <?php use Phalcon\Mvc\View; // Setting up the view component $di->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 <script type='text/javascript'> $.ajax({ url: '<?php echo $this->url->get('cities/get'); ?>' }) .done(function () { alert('Done!'); }); </script> ``` ## 独立组件 Phalcon中的所有组件都可以单独用作*胶水*组件,因为它们彼此松散耦合: ### 分层渲染 在独立模式下使用`Phalcon\Mvc\View`可以在下面演示: ```php <?php use Phalcon\Mvc\View; $view = new View(); // A trailing directory separator is required $view->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 <?php use Phalcon\Mvc\View; $view = new View(); echo $view->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 <?php use Phalcon\Mvc\View\Simple as SimpleView; $view = new SimpleView(); // A trailing directory separator is required $view->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 <?php use Phalcon\Events\Event; use Phalcon\Events\Manager as EventsManager; use Phalcon\Mvc\View; $di->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 <?php use Phalcon\Events\Event; class TidyPlugin { public function afterRender(Event $event, $view) { $tidyConfig = [ 'clean' => 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() ); ```