視圖是?MVC?模式中的一部分。 它是展示數(shù)據(jù)到終端用戶的代碼,在網(wǎng)頁應(yīng)用中,根據(jù)視圖模板來創(chuàng)建視圖,視圖模板為PHP腳本文件, 主要包含HTML代碼和展示類PHP代碼,通過yii\web\View應(yīng)用組件來管理, 該組件主要提供通用方法幫助視圖構(gòu)造和渲染,簡單起見,我們稱視圖模板或視圖模板文件為視圖。
如前所述,視圖為包含HTML和PHP代碼的PHP腳本,如下代碼為一個(gè)登錄表單的視圖, 可看到PHP代碼用來生成動(dòng)態(tài)內(nèi)容如頁面標(biāo)題和表單,HTML代碼把它組織成一個(gè)漂亮的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 來管理和渲染這個(gè)視圖文件。
除了?$this
之外,上述示例中的視圖有其他預(yù)定義變量如?$model
, 這些變量代表從控制器或其他觸發(fā)視圖渲染的對(duì)象?傳入?到視圖的數(shù)據(jù)。
技巧: 將預(yù)定義變量列到視圖文件頭部注釋處,這樣可被IDE編輯器識(shí)別,也是生成視圖文檔的好方法。
當(dāng)創(chuàng)建生成HTML頁面的視圖時(shí),在顯示之前將用戶輸入數(shù)據(jù)進(jìn)行轉(zhuǎn)碼和過濾非常重要, 否則,你的應(yīng)用可能會(huì)被跨站腳本?攻擊。
要顯示純文本,先調(diào)用 yii\helpers\Html::encode() 進(jìn)行轉(zhuǎn)碼,例如如下代碼將用戶名在顯示前先轉(zhuǎn)碼:
<?php
use yii\helpers\Html;
?>
<div class="username">
<?= Html::encode($user->name) ?>
</div>
要顯示HTML內(nèi)容,先調(diào)用 yii\helpers\HtmlPurifier 過濾內(nèi)容,例如如下代碼將提交內(nèi)容在顯示前先過濾:
<?php
use yii\helpers\HtmlPurifier;
?>
<div class="post">
<?= HtmlPurifier::process($post->text) ?>
</div>
技巧:HTMLPurifier在保證輸出數(shù)據(jù)安全上做的不錯(cuò),但性能不佳,如果你的應(yīng)用需要高性能可考慮?緩存?過濾后的結(jié)果。
@app/views/ControllerID
?目錄下, 其中?ControllerID
?對(duì)應(yīng)?控制器 ID, 例如控制器類為PostController
,視圖文件目錄應(yīng)為?@app/views/post
, 控制器類?PostCommentController
對(duì)應(yīng)的目錄為@app/views/post-comment
, 如果是模塊中的控制器,目錄應(yīng)為 yii\base\Module::basePath 模塊目錄下的views/ControllerID
?目錄;WidgetPath/views
?目錄, 其中?WidgetPath
?代表小部件類文件所在的目錄;可覆蓋控制器或小部件的 yii\base\ViewContextInterface::getViewPath() 方法來自定義視圖文件默認(rèn)目錄。
可在?控制器,?小部件, 或其他地方調(diào)用渲染視圖方法來渲染視圖, 該方法類似以下格式:
/**
* @param string $view 視圖名或文件路徑,由實(shí)際的渲染方法決定
* @param array $params 傳遞給視圖的數(shù)據(jù)
* @return string 渲染結(jié)果
*/
methodName($view, $params = [])
在?控制器?中,可調(diào)用以下控制器方法來渲染視圖:
例如:
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;
}
// 渲染一個(gè)名稱為"view"的視圖并使用布局
return $this->render('view', [
'model' => $model,
]);
}
}
在?小部件?中,可調(diào)用以下小部件方法來渲染視圖: Within?widgets, you may call the following widget methods to render views.
例如:
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class ListWidget extends Widget
{
public $items = [];
public function run()
{
// 渲染一個(gè)名為 "list" 的視圖
return $this->render('list', [
'items' => $this->items,
]);
}
}
可以在視圖中渲染另一個(gè)視圖,可以調(diào)用yii\base\View視圖組件提供的以下方法:
例如,視圖中的如下代碼會(huì)渲染該視圖所在目錄下的?_overview.php
?視圖文件, 記住視圖中?$this
?對(duì)應(yīng) yii\base\View 組件:
<?= $this->render('_overview') ?>
在任何地方都可以通過表達(dá)式?Yii::$app->view
?訪問 yii\base\View 應(yīng)用組件, 調(diào)用它的如前所述的方法渲染視圖,例如:
// 顯示視圖文件 "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
渲染視圖時(shí),可指定一個(gè)視圖名或視圖文件路徑/別名,大多數(shù)情況下使用前者因?yàn)榍罢吆啙嶌`活, 我們稱用名字的視圖為?視圖名.
視圖名可以依據(jù)以下規(guī)則到對(duì)應(yīng)的視圖文件路徑:
.php
?作為擴(kuò)展, 視圖名?about
?對(duì)應(yīng)到?about.php
?文件名;//
?開頭,對(duì)應(yīng)的視圖文件路徑為?@app/views/ViewName
, 也就是說視圖文件在 yii\base\Application::viewPath 路徑下找, 例如?//site/about
?對(duì)應(yīng)到?@app/views/site/about.php
。/
開始,視圖文件路徑以當(dāng)前使用模塊?的yii\base\Module::viewPath開始, 如果不存在模塊,使用@app/views/ViewName
開始,例如,如果當(dāng)前模塊為user
,?/user/create
?對(duì)應(yīng)成@app/modules/user/views/user/create.php
, 如果不在模塊中,/user/create
對(duì)應(yīng)@app/views/user/create.php
。SiteController
,site/about
?對(duì)應(yīng)到?@app/views/site/about.php
。@app/views/post/index.php
?渲染的?item
?對(duì)應(yīng)到?@app/views/post/item
。根據(jù)以上規(guī)則,在控制器中?app\controllers\PostController
?調(diào)用?$this->render('view')
, 實(shí)際上渲染@app/views/post/view.php
?視圖文件,當(dāng)在該視圖文件中調(diào)用?$this->render('_overview')
?會(huì)渲染@app/views/post/_overview.php
?視圖文件。
在視圖中有兩種方式訪問數(shù)據(jù):推送和拉取。
推送方式是通過視圖渲染方法的第二個(gè)參數(shù)傳遞數(shù)據(jù),數(shù)據(jù)格式應(yīng)為名稱-值的數(shù)組, 視圖渲染時(shí),調(diào)用PHP?extract()
?方法將該數(shù)組轉(zhuǎn)換為視圖可訪問的變量。 例如,如下控制器的渲染視圖代碼推送2個(gè)變量到?report
?視圖:$foo = 1
?和?$bar = 2
。
echo $this->render('report', [
'foo' => 1,
'bar' => 2,
]);
拉取方式可讓視圖從yii\base\View視圖組件或其他對(duì)象中主動(dòng)獲得數(shù)據(jù)(如Yii::$app
), 在視圖中使用如下表達(dá)式$this->context
可獲取到控制器ID, 可讓你在report
視圖中獲取控制器的任意屬性或方法,如以下代碼獲取控制器ID。
The controller ID is: <?= $this->context->id ?>
?>
推送方式讓視圖更少依賴上下文對(duì)象,是視圖獲取數(shù)據(jù)優(yōu)先使用方式, 缺點(diǎn)是需要手動(dòng)構(gòu)建數(shù)組,有些繁瑣,在不同地方渲染時(shí)容易出錯(cuò)。
yii\base\View視圖組件提供yii\base\View::params參數(shù)屬性來讓不同視圖共享數(shù)據(jù)。
例如在about
視圖中,可使用如下代碼指定當(dāng)前breadcrumbs的當(dāng)前部分。
$this->params['breadcrumbs'][] = 'About Us';
在布局文件(也是一個(gè)視圖)中,可使用依次加入到y(tǒng)ii\base\View::params數(shù)組的值來 生成顯示breadcrumbs:
<?= yii\widgets\Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
布局是一種特殊的視圖,代表多個(gè)視圖的公共部分,例如,大多數(shù)Web應(yīng)用共享相同的頁頭和頁尾, 在每個(gè)視圖中重復(fù)相同的頁頭和頁尾,更好的方式是將這些公共放到一個(gè)布局中, 渲染內(nèi)容視圖后在合適的地方嵌入到布局中。
由于布局也是視圖,它可像普通視圖一樣創(chuàng)建,布局默認(rèn)存儲(chǔ)在@app/views/layouts
路徑下,?模塊中使用的布局應(yīng)存儲(chǔ)在yii\base\Module::basePath模塊目錄 下的views/layouts
路徑下,可配置yii\base\Module::layoutPath來自定義應(yīng)用或模塊的布局默認(rèn)路徑。
如下示例為一個(gè)布局大致內(nèi)容,注意作為示例,簡化了很多代碼, 在實(shí)際中,你可能想添加更多內(nèi)容,如頭部標(biāo)簽,主菜單等。
<?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() ?>
如上所示,布局生成每個(gè)頁面通用的HTML標(biāo)簽,在<body>
標(biāo)簽中,打印$content
變量,?$content
變量代表當(dāng)yii\base\Controller::render()控制器渲染方法調(diào)用時(shí)傳遞到布局的內(nèi)容視圖渲染結(jié)果。
大多數(shù)視圖應(yīng)調(diào)用上述代碼中的如下方法,這些方法觸發(fā)關(guān)于渲染過程的事件, 這樣其他地方注冊的腳本和標(biāo)簽會(huì)添加到這些方法調(diào)用的地方。
<head>
標(biāo)簽中調(diào)用, 它生成一個(gè)占位符,在頁面渲染結(jié)束時(shí)會(huì)被注冊的頭部HTML代碼(如,link標(biāo)簽, meta標(biāo)簽)替換。<body>
標(biāo)簽的開始處調(diào)用, 它觸發(fā) yii\web\View::EVENT_BEGIN_BODY 事件并生成一個(gè)占位符, 會(huì)被注冊的HTML代碼(如JavaScript)在頁面主體開始處替換。<body>
標(biāo)簽的結(jié)尾處調(diào)用, 它觸發(fā) yii\web\View::EVENT_END_BODY 事件并生成一個(gè)占位符, 會(huì)被注冊的HTML代碼(如JavaScript)在頁面主體結(jié)尾處替換。在布局中可訪問兩個(gè)預(yù)定義變量:$this
?和?$content
,前者對(duì)應(yīng)和普通視圖類似的yii\base\View 視圖組件 后者包含調(diào)用yii\base\Controller::render()方法渲染內(nèi)容視圖的結(jié)果。
如果想在布局中訪問其他數(shù)據(jù),必須使用視圖中訪問數(shù)據(jù)一節(jié)介紹的拉取方式, 如果想從內(nèi)容視圖中傳遞數(shù)據(jù)到布局,可使用視圖間共享數(shù)據(jù)一節(jié)中的方法。
如控制器中渲染一節(jié)描述,當(dāng)控制器調(diào)用yii\base\Controller::render() 方法渲染視圖時(shí),會(huì)同時(shí)使用布局到渲染結(jié)果中,默認(rèn)會(huì)使用@app/views/layouts/main.php
布局文件。
可配置yii\base\Application::layout 或 yii\base\Controller::layout 使用其他布局文件, 前者管理所有控制器的布局,后者覆蓋前者來控制單個(gè)控制器布局。 例如,如下代碼使?post
?控制器渲染視圖時(shí)使用?@app/views/layouts/post.php
?作為布局文件, 假如layout
?屬性沒改變,控制器默認(rèn)使用?@app/views/layouts/main.php
?作為布局文件。
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public $layout = 'post';
// ...
}
對(duì)于模塊中的控制器,可配置模塊的 yii\base\Module::layout 屬性指定布局文件應(yīng)用到模塊的所有控制器。
由于layout
?可在不同層級(jí)(控制器、模塊,應(yīng)用)配置,在幕后Yii使用兩步來決定控制器實(shí)際使用的布局。
第一步,它決定布局的值和上下文模塊:
第二步,它決定第一步中布局的值和上下文模塊對(duì)應(yīng)到實(shí)際的布局文件,布局的值可為:
@app/views/layouts/main
)./main
): 布局的值以斜杠開始,在應(yīng)用的[[yii\base\Application::layoutPath|layout path] 布局路徑 中查找實(shí)際的布局文件,布局路徑默認(rèn)為?@app/views/layouts
。main
): 在上下文模塊的yii\base\Module::layoutPath布局路徑中查找實(shí)際的布局文件, 布局路徑默認(rèn)為yii\base\Module::basePath模塊目錄下的views/layouts
?目錄。false
: 不使用布局。布局的值沒有包含文件擴(kuò)展名,默認(rèn)使用?.php
作為擴(kuò)展名。
有時(shí)候你想嵌套一個(gè)布局到另一個(gè),例如,在Web站點(diǎn)不同地方,想使用不同的布局, 同時(shí)這些布局共享相同的生成全局HTML5頁面結(jié)構(gòu)的基本布局,可以在子布局中調(diào)用 yii\base\View::beginContent() 和yii\base\View::endContent() 方法,如下所示:
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
...child layout content here...
<?php $this->endContent(); ?>
如上所示,子布局內(nèi)容應(yīng)在 yii\base\View::beginContent() 和 yii\base\View::endContent() 方法之間,傳給 yii\base\View::beginContent() 的參數(shù)指定父布局,父布局可為布局文件或別名。
使用以上方式可多層嵌套布局。
數(shù)據(jù)塊可以在一個(gè)地方指定視圖內(nèi)容在另一個(gè)地方顯示,通常和布局一起使用, 例如,可在內(nèi)容視圖中定義數(shù)據(jù)塊在布局中顯示它。
調(diào)用 yii\base\View::beginBlock() 和 yii\base\View::endBlock() 來定義數(shù)據(jù)塊, 使用?$view->blocks[$blockID]
?訪問該數(shù)據(jù)塊,其中?$blockID
?為定義數(shù)據(jù)塊時(shí)指定的唯一標(biāo)識(shí)ID。
如下實(shí)例顯示如何在內(nèi)容視圖中使用數(shù)據(jù)塊讓布局使用。
首先,在內(nèi)容視圖中定一個(gè)或多個(gè)數(shù)據(jù)塊:
...
<?php $this->beginBlock('block1'); ?>
...content of block1...
<?php $this->endBlock(); ?>
...
<?php $this->beginBlock('block3'); ?>
...content of block3...
<?php $this->endBlock(); ?>
然后,在布局視圖中,數(shù)據(jù)塊可用的話會(huì)渲染數(shù)據(jù)塊,如果數(shù)據(jù)未定義則顯示一些默認(rèn)內(nèi)容。
...
<?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視圖組件提供許多視圖相關(guān)特性,可創(chuàng)建yii\base\View或它的子類實(shí)例來獲取視圖組件, 大多數(shù)情況下主要使用?view
應(yīng)用組件,可在應(yīng)用配置中配置該組件, 如下所示:
[
// ...
'components' => [
'view' => [
'class' => 'app\components\View',
],
// ...
],
]
視圖組件提供如下實(shí)用的視圖相關(guān)特性,每項(xiàng)詳情會(huì)在獨(dú)立章節(jié)中介紹:
開發(fā)Web頁面時(shí),也可能頻繁使用以下實(shí)用的小特性。
每個(gè)Web頁面應(yīng)有一個(gè)標(biāo)題,正常情況下標(biāo)題的標(biāo)簽顯示在?布局中, 但是實(shí)際上標(biāo)題大多由內(nèi)容視圖而不是布局來決定,為解決這個(gè)問題, yii\web\View 提供 yii\web\View::title 標(biāo)題屬性可讓標(biāo)題信息從內(nèi)容視圖傳遞到布局中。
為利用這個(gè)特性,在每個(gè)內(nèi)容視圖中設(shè)置頁面標(biāo)題,如下所示:
<?php
$this->title = 'My page title';
?>
然后在視圖中,確保在?<head>
?段中有如下代碼:
<title><?= Html::encode($this->title) ?></title>
Web頁面通常需要生成各種元標(biāo)簽提供給不同的瀏覽器,如<head>
中的頁面標(biāo)題,元標(biāo)簽通常在布局中生成。
如果想在內(nèi)容視圖中生成元標(biāo)簽,可在內(nèi)容視圖中調(diào)用yii\web\View::registerMetaTag()方法,如下所示:
<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>
以上代碼會(huì)在視圖組件中注冊一個(gè) "keywords" 元標(biāo)簽,在布局渲染后會(huì)渲染該注冊的元標(biāo)簽, 然后,如下HTML代碼會(huì)插入到布局中調(diào)用yii\web\View::head()方法處:
<meta name="keywords" content="yii, framework, php">
注意如果多次調(diào)用 yii\web\View::registerMetaTag() 方法,它會(huì)注冊多個(gè)元標(biāo)簽,注冊時(shí)不會(huì)檢查是否重復(fù)。
為確保每種元標(biāo)簽只有一個(gè),可在調(diào)用方法時(shí)指定鍵作為第二個(gè)參數(shù), 例如,如下代碼注冊兩次 "description" 元標(biāo)簽,但是只會(huì)渲染第二個(gè)。
$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標(biāo)簽?類似,鏈接標(biāo)簽有時(shí)很實(shí)用,如自定義網(wǎng)站圖標(biāo),指定Rss訂閱,或授權(quán)OpenID到其他服務(wù)器。 可以和元標(biāo)簽相似的方式調(diào)用yii\web\View::registerLinkTag(),例如,在內(nèi)容視圖中注冊鏈接標(biāo)簽如下所示:
$this->registerLinkTag([
'title' => 'Live News for Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'http://www.yiiframework.com/rss.xml/',
]);
上述代碼會(huì)轉(zhuǎn)換成
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" rel="external nofollow" target="_blank" >
和 yii\web\View::registerMetaTag() 類似, 調(diào)用yii\web\View::registerLinkTag() 指定鍵來避免生成重復(fù)鏈接標(biāo)簽。
yii\base\View 視圖組件會(huì)在視圖渲染過程中觸發(fā)幾個(gè)事件, 可以在內(nèi)容發(fā)送給終端用戶前,響應(yīng)這些事件來添加內(nèi)容到視圖中或調(diào)整渲染結(jié)果。
例如,如下代碼將當(dāng)前日期添加到頁面結(jié)尾處:
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});
靜態(tài)頁面指的是大部分內(nèi)容為靜態(tài)的不需要控制器傳遞動(dòng)態(tài)數(shù)據(jù)的Web頁面。
可將HTML代碼放置在視圖中,在控制器中使用以下代碼輸出靜態(tài)頁面:
public function actionAbout()
{
return $this->render('about');
}
如果Web站點(diǎn)包含很多靜態(tài)頁面,多次重復(fù)相似的代碼顯得很繁瑣, 為解決這個(gè)問題,可以使用一個(gè)在控制器中稱為 yii\web\ViewAction 的獨(dú)立操作。 例如:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actions()
{
return [
'page' => [
'class' => 'yii\web\ViewAction',
],
];
}
}
現(xiàn)在如果你在@app/views/site/pages
目錄下創(chuàng)建名為?about
?的視圖, 可通過如下rul顯示該視圖:
http://localhost/index.php?r=site/page&view=about
GET
?中?view
?參數(shù)告知 yii\web\ViewAction 操作請求哪個(gè)視圖,然后操作在?@app/views/site/pages
目錄下尋找該視圖,可配置 yii\web\ViewAction::viewPrefix 修改搜索視圖的目錄。
視圖負(fù)責(zé)將模型的數(shù)據(jù)展示用戶想要的格式,總之,視圖
$_GET
,?$_POST
,這種應(yīng)在控制器中執(zhí)行, 如果需要請求數(shù)據(jù),應(yīng)由控制器推送到視圖。為使模型更易于維護(hù),避免創(chuàng)建太復(fù)雜或包含太多冗余代碼的視圖,可遵循以下方法達(dá)到這個(gè)目標(biāo):
更多建議: