使用视图
最后更新于:2022-04-02 05:14:59
[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
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
简单地移动到文件层次结构中的下一个视图级别。如果实现了所有三个视图文件,则将按如下方式处理它们:
<!-- app/views/posts/show.phtml -->
<h3>This is show view!</h3>
<p>I have received the parameter <!--?php echo $postId; ?--></p>
<!-- app/views/layouts/posts.phtml -->
<h2>This is the "posts" controller layout!</h2>
<!--?php echo $this--->getContent(); ?>
<!-- app/views/index.phtml -->
<title>Example</title>
<h1>This is main layout!</h1>
<!--?php echo $this--->getContent(); ?>
注意调用方法$this->getContent()
的行。此方法指示Phalcon\Mvc\View
在哪里注入层次结构中执行的上一个视图的内容。对于上面的示例,输出将是:
.. figure:: ../_static/img/views-1.png :align: center
请求生成的HTML将是:
<!-- app/views/index.phtml -->
<title>Example</title>
<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>
使用模板
模板是可用于共享公共视图代码的视图。它们充当控制器布局,因此您需要将它们放在layouts目录中。
模板可以在布局之前呈现(使用$this->view->setTemplateBefore()
),也可以在布局之后渲染(this->view->setTemplateAfter()
)。在以下示例中,模板(layouts/common.phtml
)在控制器布局(layouts/posts.phtml
)之后渲染:
<!--?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'
);
}
}
<!-- app/views/index.phtml -->
<title>Blog's title</title>
<!--?php echo $this--->getContent(); ?>
<!-- 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>
<!-- app/views/layouts/posts.phtml -->
<h1>Blog Title</h1>
<!--?php echo $this--->getContent(); ?>
<!-- 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>
最终输出如下:
<!-- app/views/index.phtml -->
<title>Blog's title</title>
<!-- 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>
如果我们使用了$this->view->setTemplateBefore('common')
,那么这将是最终输出:
<!-- app/views/index.phtml -->
<title>Blog's title</title>
<!-- 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>
控制渲染级别
如上所示,Phalcon\Mvc\View
支持视图层次结构。您可能需要控制视图组件生成的渲染级别。方法Phalcon\Mvc\View::setRenderLevel()
提供此功能。
可以从控制器或从上级视图层调用此方法以干扰渲染过程。
<!--?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
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
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
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
use Phalcon\Mvc\Controller;
class UsersController extends Controller
{
public function closeSessionAction()
{
// Close session
// ...
// Disable the view to avoid rendering
$this--->view->disable();
}
}
或者,您可以返回false
以产生相同的效果:
<!--?php
use Phalcon\Mvc\Controller;
class UsersController extends Controller
{
public function closeSessionAction()
{
// ...
// Disable the view to avoid rendering
return false;
}
}
您可以返回response
对象以避免手动禁用视图:
<?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
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
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
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
$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的一种方法是将它们视为子例程的等价物:作为一种将细节移出视图的方法,以便更容易理解代码。例如,您可能有一个如下所示的视图:
<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 $this--->partial('shared/ad_banner', ['id' => $site->id, 'size' => 'big']); ?>
将值从控制器传输到视图
Phalcon\Mvc\View
在每个控制器中都可以使用视图变量($this->view
)。您可以使用该对象通过使用setVar()
方法从控制器操作直接将变量设置到视图。
<!--?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()
的第一个参数名称的变量,以供使用。变量可以是任何类型,从简单的字符串,整数等变量到更复杂的结构,如数组,集合等。
<h1>
{{ username }}'s Posts
</h1>
<div class="post">
<!--?php
foreach ($posts as $post) {
echo '<h2-->', $post->title, '';
}
?>
</div>
缓存视图片段
有时,当您开发动态网站并且其中某些区域不经常更新时,请求之间的输出完全相同。Phalcon\Mvc\View
提供缓存部分或整个渲染输出以提高性能。
Phalcon\Mvc\View
与 Phalcon\Cache
集成,提供了一种缓存输出片段的简便方法。您可以手动设置缓存处理程序或设置全局处理程序:
<!--?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哈希和当前以controller/view
格式呈现的视图自动创建一个键。为每个操作定义一个键是一个好习惯,这样您就可以轻松识别与每个视图关联的缓存。
当View组件需要缓存某些内容时,它将从服务容器中请求缓存服务。此服务的服务名称约定是viewCache
:
<!--?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
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替代站点是实现片段缓存的示例。
模板引擎
模板引擎可帮助设计人员在不使用复杂语法的情况下创建视图。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
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
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上有几个模板引擎可用的适配器
在View中注入服务
每个执行的视图都包含在Phalcon\Di\Injectable
实例中,可以轻松访问应用程序的服务容器。
以下示例说明如何使用具有框架约定的URL编写jQuery ajax请求。通过访问具有相同名称的属性,在视图中注入服务url
(通常是Phalcon\Mvc\Url
):
<script type="text/javascript">
$.ajax({
url: '<?php echo $this->url->get('cities/get'); ?>'
})
.done(function () {
alert('Done!');
});
</script>
独立组件
Phalcon中的所有组件都可以单独用作胶水组件,因为它们彼此松散耦合:
分层渲染
在独立模式下使用Phalcon\Mvc\View
可以在下面演示:
<!--?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
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
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
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创建一个清理/修复渲染过程生成的HTML的插件:
<!--?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()
);