# 視圖
視圖是?[MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)?模式中的一部分。 它是展示數據到終端用戶的代碼,在網頁應用中,根據*視圖模板*來創建視圖,視圖模板為PHP腳本文件, 主要包含HTML代碼和展示類PHP代碼,通過yii\web\View應用組件來管理, 該組件主要提供通用方法幫助視圖構造和渲染,簡單起見,我們稱視圖模板或視圖模板文件為視圖。
## 創建視圖
如前所述,視圖為包含HTML和PHP代碼的PHP腳本,如下代碼為一個登錄表單的視圖, 可看到PHP代碼用來生成動態內容如頁面標題和表單,HTML代碼把它組織成一個漂亮的HTML頁面。
~~~
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $model app\models\LoginForm */
$this->title = 'Login';
?>
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username') ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= Html::submitButton('Login') ?>
<?php ActiveForm::end(); ?>
~~~
在視圖中,可訪問?`$this`?指向 yii\web\View 來管理和渲染這個視圖文件。
除了?`$this`之外,上述示例中的視圖有其他預定義變量如?`$model`, 這些變量代表從[控制器](http://www.yiichina.com/doc/guide/2.0/structure-controllers)或其他觸發[視圖渲染](http://www.yiichina.com/doc/guide/2.0/structure-views#rendering-views)的對象?*傳入*?到視圖的數據。
> 技巧: 將預定義變量列到視圖文件頭部注釋處,這樣可被IDE編輯器識別,也是生成視圖文檔的好方法。
### 安全
當創建生成HTML頁面的視圖時,在顯示之前將用戶輸入數據進行轉碼和過濾非常重要, 否則,你的應用可能會被[跨站腳本](http://en.wikipedia.org/wiki/Cross-site_scripting)?攻擊。
要顯示純文本,先調用 yii\helpers\Html::encode() 進行轉碼,例如如下代碼將用戶名在顯示前先轉碼:
~~~
<?php
use yii\helpers\Html;
?>
<div class="username">
<?= Html::encode($user->name) ?>
</div>
~~~
要顯示HTML內容,先調用 yii\helpers\HtmlPurifier 過濾內容,例如如下代碼將提交內容在顯示前先過濾:
~~~
<?php
use yii\helpers\HtmlPurifier;
?>
<div class="post">
<?= HtmlPurifier::process($post->text) ?>
</div>
~~~
> 技巧:HTMLPurifier在保證輸出數據安全上做的不錯,但性能不佳,如果你的應用需要高性能可考慮?[緩存](http://www.yiichina.com/doc/guide/2.0/caching-overview)?過濾后的結果。
### 組織視圖
與?[控制器](http://www.yiichina.com/doc/guide/2.0/structure-controllers)?和?[模型](http://www.yiichina.com/doc/guide/2.0/structure-models)?類似,在組織視圖上有一些約定:
* 控制器渲染的視圖文件默認放在?`@app/views/ControllerID`?目錄下, 其中?`ControllerID`?對應?[控制器 ID](http://www.yiichina.com/doc/guide/2.0/structure-controllers#routes), 例如控制器類為`PostController`,視圖文件目錄應為?`@app/views/post`, 控制器類?`PostCommentController`對應的目錄為`@app/views/post-comment`, 如果是模塊中的控制器,目錄應為 yii\base\Module::basePath 模塊目錄下的`views/ControllerID`?目錄;
* 對于?[小部件](http://www.yiichina.com/doc/guide/2.0/structure-widgets)?渲染的視圖文件默認放在?`WidgetPath/views`?目錄, 其中?`WidgetPath`?代表小部件類文件所在的目錄;
* 對于其他對象渲染的視圖文件,建議遵循和小部件相似的規則。
可覆蓋控制器或小部件的 yii\base\ViewContextInterface::getViewPath() 方法來自定義視圖文件默認目錄。
## 渲染視圖
可在?[控制器](http://www.yiichina.com/doc/guide/2.0/structure-controllers),?[小部件](http://www.yiichina.com/doc/guide/2.0/structure-widgets), 或其他地方調用渲染視圖方法來渲染視圖, 該方法類似以下格式:
~~~
/**
* @param string $view 視圖名或文件路徑,由實際的渲染方法決定
* @param array $params 傳遞給視圖的數據
* @return string 渲染結果
*/
methodName($view, $params = [])
~~~
### 控制器中渲染
在?[控制器](http://www.yiichina.com/doc/guide/2.0/structure-controllers)?中,可調用以下控制器方法來渲染視圖:
* yii\base\Controller::render(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views)?并使用一個?[布局](http://www.yiichina.com/doc/guide/2.0/structure-views#layouts)?返回到渲染結果。
* yii\base\Controller::renderPartial(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views)?并且不使用布局。
* yii\web\Controller::renderAjax(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views)?并且不使用布局, 并注入所有注冊的JS/CSS腳本和文件,通常使用在響應AJAX網頁請求的情況下。
* yii\base\Controller::renderFile(): 渲染一個視圖文件目錄或[別名](http://www.yiichina.com/doc/guide/2.0/concept-aliases)下的視圖文件。
例如:
~~~
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
// 渲染一個名稱為"view"的視圖并使用布局
return $this->render('view', [
'model' => $model,
]);
}
}
~~~
### 小部件中渲染
在?[小部件](http://www.yiichina.com/doc/guide/2.0/structure-widgets)?中,可調用以下小部件方法來渲染視圖: Within?[widgets](http://www.yiichina.com/doc/guide/2.0/structure-widgets), you may call the following widget methods to render views.
* yii\base\Widget::render(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views).
* yii\base\Widget::renderFile(): 渲染一個視圖文件目錄或[別名](http://www.yiichina.com/doc/guide/2.0/concept-aliases)下的視圖文件。
例如:
~~~
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class ListWidget extends Widget
{
public $items = [];
public function run()
{
// 渲染一個名為 "list" 的視圖
return $this->render('list', [
'items' => $this->items,
]);
}
}
~~~
### 視圖中渲染
可以在視圖中渲染另一個視圖,可以調用yii\base\View視圖組件提供的以下方法:
* yii\base\View::render(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views).
* yii\web\View::renderAjax(): 渲染一個?[視圖名](http://www.yiichina.com/doc/guide/2.0/structure-views#named-views)?并注入所有注冊的JS/CSS腳本和文件,通常使用在響應AJAX網頁請求的情況下。
* yii\base\View::renderFile(): 渲染一個視圖文件目錄或[別名](http://www.yiichina.com/doc/guide/2.0/concept-aliases)下的視圖文件。
例如,視圖中的如下代碼會渲染該視圖所在目錄下的?`_overview.php`?視圖文件, 記住視圖中?`$this`?對應 yii\base\View 組件:
~~~
<?= $this->render('_overview') ?>
~~~
### 其他地方渲染
在任何地方都可以通過表達式?`Yii::$app->view`?訪問 yii\base\View 應用組件, 調用它的如前所述的方法渲染視圖,例如:
~~~
// 顯示視圖文件 "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
~~~
### 視圖名
渲染視圖時,可指定一個視圖名或視圖文件路徑/別名,大多數情況下使用前者因為前者簡潔靈活, 我們稱用名字的視圖為?*視圖名*.
視圖名可以依據以下規則到對應的視圖文件路徑:
* 視圖名可省略文件擴展名,這種情況下使用?`.php`?作為擴展, 視圖名?`about`?對應到?`about.php`?文件名;
* 視圖名以雙斜杠?`//`?開頭,對應的視圖文件路徑為?`@app/views/ViewName`, 也就是說視圖文件在 yii\base\Application::viewPath 路徑下找, 例如?`//site/about`?對應到?`@app/views/site/about.php`。
* 視圖名以單斜杠`/`開始,視圖文件路徑以當前使用[模塊](http://www.yiichina.com/doc/guide/2.0/structure-modules)?的yii\base\Module::viewPath開始, 如果不存在模塊,使用`@app/views/ViewName`開始,例如,如果當前模塊為`user`,?`/user/create`?對應成`@app/modules/user/views/user/create.php`, 如果不在模塊中,`/user/create`對應`@app/views/user/create.php`。
* 如果 yii\base\View::context 渲染視圖 并且上下文實現了 yii\base\ViewContextInterface, 視圖文件路徑由上下文的 yii\base\ViewContextInterface::getViewPath() 開始, 這種主要用在控制器和小部件中渲染視圖,例如 如果上下文為控制器`SiteController`,`site/about`?對應到?`@app/views/site/about.php`。
* 如果視圖渲染另一個視圖,包含另一個視圖文件的目錄以當前視圖的文件路徑開始, 例如被視圖`@app/views/post/index.php`?渲染的?`item`?對應到?`@app/views/post/item`。
根據以上規則,在控制器中?`app\controllers\PostController`?調用?`$this->render('view')`, 實際上渲染`@app/views/post/view.php`?視圖文件,當在該視圖文件中調用?`$this->render('_overview')`?會渲染`@app/views/post/_overview.php`?視圖文件。
### 視圖中訪問數據
在視圖中有兩種方式訪問數據:推送和拉取。
推送方式是通過視圖渲染方法的第二個參數傳遞數據,數據格式應為名稱-值的數組, 視圖渲染時,調用PHP?`extract()`?方法將該數組轉換為視圖可訪問的變量。 例如,如下控制器的渲染視圖代碼推送2個變量到?`report`?視圖:`$foo = 1`?和?`$bar = 2`。
~~~
echo $this->render('report', [
'foo' => 1,
'bar' => 2,
]);
~~~
拉取方式可讓視圖從yii\base\View視圖組件或其他對象中主動獲得數據(如`Yii::$app`), 在視圖中使用如下表達式`$this->context`可獲取到控制器ID, 可讓你在`report`視圖中獲取控制器的任意屬性或方法,如以下代碼獲取控制器ID。
~~~
The controller ID is: <?= $this->context->id ?>
?>
~~~
推送方式讓視圖更少依賴上下文對象,是視圖獲取數據優先使用方式, 缺點是需要手動構建數組,有些繁瑣,在不同地方渲染時容易出錯。
### 視圖間共享數據
yii\base\View視圖組件提供yii\base\View::params參數屬性來讓不同視圖共享數據。
例如在`about`視圖中,可使用如下代碼指定當前breadcrumbs的當前部分。
~~~
$this->params['breadcrumbs'][] = 'About Us';
~~~
在[布局](http://www.yiichina.com/doc/guide/2.0/structure-views#layouts)文件(也是一個視圖)中,可使用依次加入到yii\base\View::params數組的值來 生成顯示breadcrumbs:
~~~
<?= yii\widgets\Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
~~~
## 布局
布局是一種特殊的視圖,代表多個視圖的公共部分,例如,大多數Web應用共享相同的頁頭和頁尾, 在每個視圖中重復相同的頁頭和頁尾,更好的方式是將這些公共放到一個布局中, 渲染內容視圖后在合適的地方嵌入到布局中。
### 創建布局
由于布局也是視圖,它可像普通視圖一樣創建,布局默認存儲在`@app/views/layouts`路徑下,?[模塊](http://www.yiichina.com/doc/guide/2.0/structure-modules)中使用的布局應存儲在yii\base\Module::basePath模塊目錄 下的`views/layouts`路徑下,可配置yii\base\Module::layoutPath來自定義應用或模塊的布局默認路徑。
如下示例為一個布局大致內容,注意作為示例,簡化了很多代碼, 在實際中,你可能想添加更多內容,如頭部標簽,主菜單等。
~~~
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $content string 字符串 */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<?= Html::csrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<header>My Company</header>
<?= $content ?>
<footer>© 2014 by My Company</footer>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
~~~
如上所示,布局生成每個頁面通用的HTML標簽,在`<body>`標簽中,打印`$content`變量,?`$content`變量代表當yii\base\Controller::render()控制器渲染方法調用時傳遞到布局的內容視圖渲染結果。
大多數視圖應調用上述代碼中的如下方法,這些方法觸發關于渲染過程的事件, 這樣其他地方注冊的腳本和標簽會添加到這些方法調用的地方。
* yii\base\View::beginPage(): 該方法應在布局的開始處調用, 它觸發表明頁面開始的 yii\base\View::EVENT_BEGIN_PAGE 事件。
* yii\base\View::endPage(): 該方法應在布局的結尾處調用, 它觸發表明頁面結尾的 yii\base\View::EVENT_END_PAGE 時間。
* yii\web\View::head(): 該方法應在HTML頁面的`<head>`標簽中調用, 它生成一個占位符,在頁面渲染結束時會被注冊的頭部HTML代碼(如,link標簽, meta標簽)替換。
* yii\web\View::beginBody(): 該方法應在`<body>`標簽的開始處調用, 它觸發 yii\web\View::EVENT_BEGIN_BODY 事件并生成一個占位符, 會被注冊的HTML代碼(如JavaScript)在頁面主體開始處替換。
* yii\web\View::endBody(): 該方法應在`<body>`標簽的結尾處調用, 它觸發 yii\web\View::EVENT_END_BODY 事件并生成一個占位符, 會被注冊的HTML代碼(如JavaScript)在頁面主體結尾處替換。
### 布局中訪問數據
在布局中可訪問兩個預定義變量:`$this`?和?`$content`,前者對應和普通視圖類似的yii\base\View 視圖組件 后者包含調用yii\base\Controller::render()方法渲染內容視圖的結果。
如果想在布局中訪問其他數據,必須使用[視圖中訪問數據](http://www.yiichina.com/doc/guide/2.0/structure-views#accessing-data-in-views)一節介紹的拉取方式, 如果想從內容視圖中傳遞數據到布局,可使用[視圖間共享數據](http://www.yiichina.com/doc/guide/2.0/structure-views#sharing-data-among-views)一節中的方法。
### 使用布局
如[控制器中渲染](http://www.yiichina.com/doc/guide/2.0/structure-views#rendering-in-controllers)一節描述,當控制器調用yii\base\Controller::render() 方法渲染視圖時,會同時使用布局到渲染結果中,默認會使用`@app/views/layouts/main.php`布局文件。
可配置yii\base\Application::layout 或 yii\base\Controller::layout 使用其他布局文件, 前者管理所有控制器的布局,后者覆蓋前者來控制單個控制器布局。 例如,如下代碼使?`post`?控制器渲染視圖時使用?`@app/views/layouts/post.php`?作為布局文件, 假如`layout`?屬性沒改變,控制器默認使用?`@app/views/layouts/main.php`?作為布局文件。
~~~
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public $layout = 'post';
// ...
}
~~~
對于模塊中的控制器,可配置模塊的 yii\base\Module::layout 屬性指定布局文件應用到模塊的所有控制器。
由于`layout`?可在不同層級(控制器、模塊,應用)配置,在幕后Yii使用兩步來決定控制器實際使用的布局。
第一步,它決定布局的值和上下文模塊:
* 如果控制器的 yii\base\Controller::layout 屬性不為空null,使用它作為布局的值, 控制器的 yii\base\Controller::module模塊 作為上下文模塊。
* 如果 yii\base\Controller::layout 為空,從控制器的祖先模塊(包括應用) 開始找 第一個yii\base\Module::layout 屬性不為空的模塊,使用該模塊作為上下文模塊, 并將它的yii\base\Module::layout 的值作為布局的值, 如果都沒有找到,表示不使用布局。
第二步,它決定第一步中布局的值和上下文模塊對應到實際的布局文件,布局的值可為:
* 路徑別名 (如?`@app/views/layouts/main`).
* 絕對路徑 (如?`/main`): 布局的值以斜杠開始,在應用的[[yii\base\Application::layoutPath|layout path] 布局路徑 中查找實際的布局文件,布局路徑默認為?`@app/views/layouts`。
* 相對路徑 (如?`main`): 在上下文模塊的yii\base\Module::layoutPath布局路徑中查找實際的布局文件, 布局路徑默認為yii\base\Module::basePath模塊目錄下的`views/layouts`?目錄。
* 布爾值?`false`: 不使用布局。
布局的值沒有包含文件擴展名,默認使用?`.php`作為擴展名。
### 嵌套布局
有時候你想嵌套一個布局到另一個,例如,在Web站點不同地方,想使用不同的布局, 同時這些布局共享相同的生成全局HTML5頁面結構的基本布局,可以在子布局中調用 yii\base\View::beginContent() 和yii\base\View::endContent() 方法,如下所示:
~~~
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
...child layout content here...
<?php $this->endContent(); ?>
~~~
如上所示,子布局內容應在 yii\base\View::beginContent() 和 yii\base\View::endContent() 方法之間,傳給 yii\base\View::beginContent() 的參數指定父布局,父布局可為布局文件或別名。
使用以上方式可多層嵌套布局。
### 使用數據塊
數據塊可以在一個地方指定視圖內容在另一個地方顯示,通常和布局一起使用, 例如,可在內容視圖中定義數據塊在布局中顯示它。
調用 yii\base\View::beginBlock() 和 yii\base\View::endBlock() 來定義數據塊, 使用?`$view->blocks[$blockID]`?訪問該數據塊,其中?`$blockID`?為定義數據塊時指定的唯一標識ID。
如下實例顯示如何在內容視圖中使用數據塊讓布局使用。
首先,在內容視圖中定一個或多個數據塊:
~~~
...
<?php $this->beginBlock('block1'); ?>
...content of block1...
<?php $this->endBlock(); ?>
...
<?php $this->beginBlock('block3'); ?>
...content of block3...
<?php $this->endBlock(); ?>
~~~
然后,在布局視圖中,數據塊可用的話會渲染數據塊,如果數據未定義則顯示一些默認內容。
~~~
...
<?php if (isset($this->blocks['block1'])): ?>
<?= $this->blocks['block1'] ?>
<?php else: ?>
... default content for block1 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block2'])): ?>
<?= $this->blocks['block2'] ?>
<?php else: ?>
... default content for block2 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block3'])): ?>
<?= $this->blocks['block3'] ?>
<?php else: ?>
... default content for block3 ...
<?php endif; ?>
...
~~~
## 使用視圖組件
yii\base\View視圖組件提供許多視圖相關特性,可創建yii\base\View或它的子類實例來獲取視圖組件, 大多數情況下主要使用?`view`應用組件,可在[應用配置](http://www.yiichina.com/doc/guide/2.0/structure-applications#application-configurations)中配置該組件, 如下所示:
~~~
[
// ...
'components' => [
'view' => [
'class' => 'app\components\View',
],
// ...
],
]
~~~
視圖組件提供如下實用的視圖相關特性,每項詳情會在獨立章節中介紹:
* [主題](http://www.yiichina.com/doc/guide/2.0/output-theming): 允許為你的Web站點開發和修改主題;
* [片段緩存](http://www.yiichina.com/doc/guide/2.0/caching-fragment): 允許你在Web頁面中緩存片段;
* [客戶腳本處理](http://www.yiichina.com/doc/guide/2.0/output-client-scripts): 支持CSS 和 JavaScript 注冊和渲染;
* [資源包處理](http://www.yiichina.com/doc/guide/2.0/structure-assets): 支持?[資源包](http://www.yiichina.com/doc/guide/2.0/structure-assets)的注冊和渲染;
* [模板引擎](http://www.yiichina.com/doc/guide/2.0/tutorial-template-engines): 允許你使用其他模板引擎,如?[Twig](http://twig.sensiolabs.org/),?[Smarty](http://www.smarty.net/)。
開發Web頁面時,也可能頻繁使用以下實用的小特性。
### 設置頁面標題
每個Web頁面應有一個標題,正常情況下標題的標簽顯示在?[布局](http://www.yiichina.com/doc/guide/2.0/structure-views#layouts)中, 但是實際上標題大多由內容視圖而不是布局來決定,為解決這個問題, yii\web\View 提供 yii\web\View::title 標題屬性可讓標題信息從內容視圖傳遞到布局中。
為利用這個特性,在每個內容視圖中設置頁面標題,如下所示:
~~~
<?php
$this->title = 'My page title';
?>
~~~
然后在視圖中,確保在?`<head>`?段中有如下代碼:
~~~
<title><?= Html::encode($this->title) ?></title>
~~~
### 注冊Meta元標簽
Web頁面通常需要生成各種元標簽提供給不同的瀏覽器,如`<head>`中的頁面標題,元標簽通常在布局中生成。
如果想在內容視圖中生成元標簽,可在內容視圖中調用yii\web\View::registerMetaTag()方法,如下所示:
~~~
<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>
~~~
以上代碼會在視圖組件中注冊一個 "keywords" 元標簽,在布局渲染后會渲染該注冊的元標簽, 然后,如下HTML代碼會插入到布局中調用yii\web\View::head()方法處:
~~~
<meta name="keywords" content="yii, framework, php">
~~~
注意如果多次調用 yii\web\View::registerMetaTag() 方法,它會注冊多個元標簽,注冊時不會檢查是否重復。
為確保每種元標簽只有一個,可在調用方法時指定鍵作為第二個參數, 例如,如下代碼注冊兩次 "description" 元標簽,但是只會渲染第二個。
~~~
$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');
$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');
~~~
### 注冊鏈接標簽
和?[Meta標簽](http://www.yiichina.com/doc/guide/2.0/structure-views#adding-meta-tags)?類似,鏈接標簽有時很實用,如自定義網站圖標,指定Rss訂閱,或授權OpenID到其他服務器。 可以和元標簽相似的方式調用yii\web\View::registerLinkTag(),例如,在內容視圖中注冊鏈接標簽如下所示:
~~~
$this->registerLinkTag([
'title' => 'Live News for Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'http://www.yiiframework.com/rss.xml/',
]);
~~~
上述代碼會轉換成
~~~
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml/">
~~~
和 yii\web\View::registerMetaTag() 類似, 調用yii\web\View::registerLinkTag() 指定鍵來避免生成重復鏈接標簽。
## 視圖事件
yii\base\View 視圖組件會在視圖渲染過程中觸發幾個事件, 可以在內容發送給終端用戶前,響應這些事件來添加內容到視圖中或調整渲染結果。
* yii\base\View::EVENT_BEFORE_RENDER: 在控制器渲染文件開始時觸發, 該事件可設置 yii\base\ViewEvent::isValid 為 false 取消視圖渲染。
* yii\base\View::EVENT_AFTER_RENDER: 在布局中調用 yii\base\View::beginPage() 時觸發, 該事件可獲取yii\base\ViewEvent::output的渲染結果,可修改該屬性來修改渲染結果。
* yii\base\View::EVENT_BEGIN_PAGE: 在布局調用 yii\base\View::beginPage() 時觸發;
* yii\base\View::EVENT_END_PAGE: 在布局調用 yii\base\View::endPage() 是觸發;
* yii\web\View::EVENT_BEGIN_BODY: 在布局調用 yii\web\View::beginBody() 時觸發;
* yii\web\View::EVENT_END_BODY: 在布局調用 yii\web\View::endBody() 時觸發。
例如,如下代碼將當前日期添加到頁面結尾處:
~~~
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});
~~~
## 渲染靜態頁面
靜態頁面指的是大部分內容為靜態的不需要控制器傳遞動態數據的Web頁面。
可將HTML代碼放置在視圖中,在控制器中使用以下代碼輸出靜態頁面:
~~~
public function actionAbout()
{
return $this->render('about');
}
~~~
如果Web站點包含很多靜態頁面,多次重復相似的代碼顯得很繁瑣, 為解決這個問題,可以使用一個在控制器中稱為 yii\web\ViewAction 的[獨立操作](http://www.yiichina.com/doc/guide/2.0/structure-controllers#standalone-actions)。 例如:
~~~
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actions()
{
return [
'page' => [
'class' => 'yii\web\ViewAction',
],
];
}
}
~~~
現在如果你在`@app/views/site/pages`目錄下創建名為?`about`?的視圖, 可通過如下rul顯示該視圖:
~~~
http://localhost/index.php?r=site/page&view=about
~~~
`GET`?中?`view`?參數告知 yii\web\ViewAction 操作請求哪個視圖,然后操作在?`@app/views/site/pages`目錄下尋找該視圖,可配置 yii\web\ViewAction::viewPrefix 修改搜索視圖的目錄。
## 最佳實踐
視圖負責將模型的數據展示用戶想要的格式,總之,視圖
* 應主要包含展示代碼,如HTML, 和簡單的PHP代碼來控制、格式化和渲染數據;
* 不應包含執行數據查詢代碼,這種代碼放在模型中;
* 應避免直接訪問請求數據,如?`$_GET`,?`$_POST`,這種應在控制器中執行, 如果需要請求數據,應由控制器推送到視圖。
* 可讀取模型屬性,但不應修改它們。
為使模型更易于維護,避免創建太復雜或包含太多冗余代碼的視圖,可遵循以下方法達到這個目標:
* 使用?[布局](http://www.yiichina.com/doc/guide/2.0/structure-views#layouts)?來展示公共代碼(如,頁面頭部、尾部);
* 將復雜的視圖分成幾個小視圖,可使用上面描述的渲染方法將這些小視圖渲染并組裝成大視圖;
* 創建并使用?[小部件](http://www.yiichina.com/doc/guide/2.0/structure-widgets)?作為視圖的數據塊;
* 創建并使用助手類在視圖中轉換和格式化數據。
- 介紹(Introduction)
- 關于 Yii(About Yii)
- 從 Yii 1.1 升級(Upgrading from Version 1.1)
- 入門(Getting Started)
- 安裝 Yii(Installing Yii)
- 運行應用(Running Applications)
- 第一次問候(Saying Hello)
- 使用 Forms(Working with Forms)
- 玩轉 Databases(Working with Databases)
- 用 Gii 生成代碼(Generating Code with Gii)
- 更上一層樓(Looking Ahead)
- 應用結構(Application Structure)
- 結構概述(Overview)
- 入口腳本(Entry Scripts)
- 應用(Applications)
- 應用組件(Application Components)
- 控制器(Controllers)
- 模型(Models)
- 視圖(Views)
- 模塊(Modules)
- 過濾器(Filters)
- 小部件(Widgets)
- 前端資源(Assets)
- 擴展(Extensions)
- 請求處理(Handling Requests)
- 運行概述(Overview)
- 引導(Bootstrapping)
- 路由引導與創建 URL(Routing and URL Creation)
- 請求(Requests)
- 響應(Responses)
- Sessions and Cookies
- 錯誤處理(Handling Errors)
- 日志(Logging)
- 關鍵概念(Key Concepts)
- 組件(Components)
- 屬性(Properties)
- 事件(Events)
- 行為(Behaviors)
- 配置(Configurations)
- 別名(Aliases)
- 類自動加載(Class Autoloading)
- 服務定位器(Service Locator)
- 依賴注入容器(Dependency Injection Container)
- 配合數據庫工作(Working with Databases)
- 數據庫訪問(Data Access Objects): 數據庫連接、基本查詢、事務和模式操作
- 查詢生成器(Query Builder): 使用簡單抽象層查詢數據庫
- 活動記錄(Active Record): 活動記錄對象關系映射(ORM),檢索和操作記錄、定義關聯關系
- 數據庫遷移(Migrations): 在團體開發中對你的數據庫使用版本控制
- Sphinx
- Redis
- MongoDB
- ElasticSearch
- 接收用戶數據(Getting Data from Users)
- 創建表單(Creating Forms)
- 輸入驗證(Validating Input)
- 文件上傳(Uploading Files)
- 收集列表輸入(Collecting Tabular Input)
- 多模型同時輸入(Getting Data for Multiple Models)
- 顯示數據(Displaying Data)
- 格式化輸出數據(Data Formatting)
- 分頁(Pagination)
- 排序(Sorting)
- 數據提供器(Data Providers)
- 數據小部件(Data Widgets)
- 操作客戶端腳本(Working with Client Scripts)
- 主題(Theming)
- 安全(Security)
- 認證(Authentication)
- 授權(Authorization)
- 處理密碼(Working with Passwords)
- 客戶端認證(Auth Clients)
- 安全領域的最佳實踐(Best Practices)
- 緩存(Caching)
- 概述(Overview)
- 數據緩存(Data Caching)
- 片段緩存(Fragment Caching)
- 分頁緩存(Page Caching)
- HTTP 緩存(HTTP Caching)
- RESTful Web 服務
- 快速入門(Quick Start)
- 資源(Resources)
- 控制器(Controllers)
- 路由(Routing)
- 格式化響應(Response Formatting)
- 授權驗證(Authentication)
- 速率限制(Rate Limiting)
- 版本化(Versioning)
- 錯誤處理(Error Handling)
- 開發工具(Development Tools)
- 調試工具欄和調試器(Debug Toolbar and Debugger)
- 使用 Gii 生成代碼(Generating Code using Gii)
- TBD 生成 API 文檔(Generating API Documentation)
- 測試(Testing)
- 概述(Overview)
- 搭建測試環境(Testing environment setup)
- 單元測試(Unit Tests)
- 功能測試(Functional Tests)
- 驗收測試(Acceptance Tests)
- 測試夾具(Fixtures)
- 高級專題(Special Topics)
- 高級應用模版(Advanced Project Template)
- 從頭構建自定義模版(Building Application from Scratch)
- 控制臺命令(Console Commands)
- 核心驗證器(Core Validators)
- 國際化(Internationalization)
- 收發郵件(Mailing)
- 性能優化(Performance Tuning)
- 共享主機環境(Shared Hosting Environment)
- 模板引擎(Template Engines)
- 集成第三方代碼(Working with Third-Party Code)
- 小部件(Widgets)
- Bootstrap 小部件(Bootstrap Widgets)
- jQuery UI 小部件(jQuery UI Widgets)
- 助手類(Helpers)
- 助手一覽(Overview)
- Array 助手(ArrayHelper)
- Html 助手(Html)
- Url 助手(Url)