ビューはMVCアーキテクチャの一部です。それらはエンドユーザーにデータを表示する役割を担うコードです。Webアプリケーションでは、ビューは通常、主にHTMLコードとプレゼンテーション用のPHPコードを含むPHPスクリプトファイルである*ビューテンプレート*の形で作成されます。それらは、ビューの構成とレンダリングを容易にするために一般的に使用されるメソッドを提供する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
にアクセスできます。
$this
に加えて、上記の例の$model
のように、ビューに他の定義済み変数がある場合があります。これらの変数は、コントローラまたはビューレンダリングをトリガーするその他のオブジェクトによってビューに*プッシュ*されるデータを表します。
ヒント: 定義済み変数は、IDEで認識できるように、ビューの先頭にあるコメントブロックにリストされています。これは、ビューをドキュメント化する良い方法でもあります。
HTMLページを生成するビューを作成するときは、エンドユーザーからのデータを表示する前に、エンコードまたはフィルタリングすることが重要です。そうしないと、アプリケーションがクロスサイトスクリプティング攻撃にさらされる可能性があります。
プレーンテキストを表示するには、最初に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は出力の安全性を確保する上で優れた役割を果たしますが、高速ではありません。アプリケーションに高いパフォーマンスが必要な場合は、フィルタリング結果をキャッシュすることを検討してください。
コントローラやモデルと同様に、ビューを構成するための規則があります。
@app/views/ControllerID
ディレクトリに配置する必要があります。ここで ControllerID
はコントローラ ID を指します。例えば、コントローラクラスが PostController
であれば、ディレクトリは @app/views/post
になり、PostCommentController
であれば、ディレクトリは @app/views/post-comment
になります。コントローラがモジュールに属している場合、ディレクトリはモジュールディレクトリ下の views/ControllerID
になります。WidgetPath/views
ディレクトリに配置する必要があります。ここで WidgetPath
はウィジェットクラスファイルを含むディレクトリを表します。コントローラまたはウィジェットのyii\base\ViewContextInterface::getViewPath() メソッドをオーバーライドすることで、これらのデフォルトのビューディレクトリをカスタマイズできます。
コントローラ、ウィジェット、またはその他の場所でビューをレンダリングするには、ビューレンダリングメソッドを呼び出します。これらのメソッドは、次のような類似のシグネチャを共有しています。
/**
* @param string $view view name or file path, depending on the actual rendering method
* @param array $params the data to be passed to the view
* @return string rendering result
*/
methodName($view, $params = [])
コントローラ内では、次のコントローラメソッドを呼び出してビューをレンダリングできます。
例えば、
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;
}
// renders a view named "view" and applies a layout to it
return $this->render('view', [
'model' => $model,
]);
}
}
ウィジェット内では、次のウィジェットメソッドを呼び出してビューをレンダリングできます。
例えば、
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class ListWidget extends Widget
{
public $items = [];
public function run()
{
// renders a view named "list"
return $this->render('list', [
'items' => $this->items,
]);
}
}
ビューコンポーネントによって提供される次のいずれかのメソッドを呼び出すことで、別のビュー内でビューをレンダリングできます。
例えば、次のビュー内のコードは、現在レンダリングされているビューと同じディレクトリにある _overview.php
ビューファイルをレンダリングします。ビュー内の $this
はビューコンポーネントを参照することに注意してください。
<?= $this->render('_overview') ?>
どの場所からでも、式 Yii::$app->view
でビューアプリケーションコンポーネントにアクセスし、その前述のメソッドを呼び出してビューをレンダリングできます。例えば、
// displays the view file "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
ビューをレンダリングするときは、ビュー名またはビューファイルパス/エイリアスのいずれかを使用してビューを指定できます。ほとんどの場合、より簡潔で柔軟性があるため、前者を使用します。名前を使用して指定されたビューを *名前付きビュー* と呼びます。
ビュー名は、次の規則に従って対応するビューファイルパスに解決されます。
.php
が使用されます。例えば、ビュー名 about
はファイル名 about.php
に対応します。//
で始まる場合、対応するビューファイルパスは @app/views/ViewName
になります。つまり、ビューはアプリケーションのビューパスの下で検索されます。例えば、//site/about
は @app/views/site/about.php
に解決されます。/
で始まる場合、ビューファイルパスは、アクティブなモジュールのビューパスをビュー名の先頭に追加することで形成されます。アクティブなモジュールがない場合、@app/views/ViewName
が使用されます。例えば、現在アクティブなモジュールが user
の場合、/user/create
は @app/modules/user/views/user/create.php
に解決されます。アクティブなモジュールがない場合、ビューファイルパスは @app/views/user/create.php
になります。SiteController
の場合、about
は @app/views/site/about.php
に解決されます。item
が @app/views/post/index.php
ビュー内でレンダリングされている場合、@app/views/post/item.php
に解決されます。上記の規則に従って、コントローラ app\controllers\PostController
で $this->render('view')
を呼び出すと、実際にはビューファイル @app/views/post/view.php
がレンダリングされ、そのビューで $this->render('_overview')
を呼び出すと、ビューファイル @app/views/post/_overview.php
がレンダリングされます。
ビュー内のデータにアクセスする方法は、プッシュとプルの2つがあります。
ビューレンダリングメソッドの2番目のパラメータとしてデータを渡すことで、プッシュアプローチを使用しています。データは、名前と値のペアの配列として表す必要があります。ビューがレンダリングされるとき、PHPの extract()
関数がこの配列に対して呼び出され、配列がビュー内の変数に展開されます。例えば、コントローラ内の次のビューレンダリングコードは、$foo = 1
と $bar = 2
の2つの変数を report
ビューにプッシュします。
echo $this->render('report', [
'foo' => 1,
'bar' => 2,
]);
プルアプローチは、ビューコンポーネントまたはビューでアクセス可能な他のオブジェクト(例えば、Yii::$app
)からデータを積極的に取得します。以下のコードを例として使用すると、ビュー内では式 $this->context
でコントローラオブジェクトを取得できます。その結果、report
ビューで、次のコントローラIDに示されているように、コントローラの任意のプロパティまたはメソッドにアクセスできます。
The controller ID is: <?= $this->context->id ?>
プッシュアプローチは、ビューをコンテキストオブジェクトに依存させないため、通常、ビューでデータにアクセスする方法として推奨されます。その欠点は、常に手動でデータ配列を構築する必要があることであり、ビューが共有され、異なる場所でレンダリングされる場合は面倒でエラーが発生しやすくなる可能性があります。
ビューコンポーネントは、ビュー間でデータを共有するために使用できるparamsプロパティを提供します。
例えば、about
ビューでは、ブレッドクラムの現在のセグメントを指定する次のコードを使用できます。
$this->params['breadcrumbs'][] = 'About Us';
次に、レイアウトファイル(これもビューです)で、paramsで渡されたデータを使用して、ブレッドクラムを表示できます。
<?= yii\widgets\Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
レイアウトは、複数のビューの共通部分を表す特別なタイプのビューです。例えば、ほとんどのWebアプリケーションのページは、同じページヘッダーとフッターを共有しています。すべてのビューで同じページヘッダーとフッターを繰り返すこともできますが、より良い方法は、レイアウトでこれを1回行い、レイアウトの適切な場所にコンテンツビューのレンダリング結果を埋め込むことです。
レイアウトもビューであるため、通常のビューと同様の方法で作成できます。デフォルトでは、レイアウトは @app/views/layouts
ディレクトリに格納されます。モジュール内で使用されるレイアウトについては、モジュールディレクトリ下の views/layouts
ディレクトリに格納する必要があります。アプリケーションまたはモジュールのyii\base\Module::$layoutPathプロパティを構成することで、デフォルトのレイアウトディレクトリをカスタマイズできます。
次の例は、レイアウトがどのように見えるかを示しています。説明のために、レイアウトのコードを大幅に簡略化していることに注意してください。実際には、headタグ、メインメニューなど、より多くのコンテンツを追加する必要があるかもしれません。
<?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
変数をエコーします。この変数は、yii\base\Controller::render() が呼び出されたときにレイアウトにプッシュされます。
ほとんどのレイアウトは、上記のコードに示すように、次のメソッドを呼び出す必要があります。これらのメソッドは、主にレンダリングプロセスに関するイベントをトリガーし、他の場所に登録されたスクリプトとタグが、これらのメソッドが呼び出された場所に適切に挿入されるようにします。
<head>
セクション内で呼び出す必要があります。ページがレンダリングを終了すると、登録されたhead HTMLコード(例えば、linkタグ、metaタグ)に置き換えられるプレースホルダーを生成します。<body>
セクションの開始時に呼び出す必要があります。EVENT_BEGIN_BODYイベントをトリガーし、ボディの開始位置をターゲットとする登録されたHTMLコード(例えば、JavaScript)に置き換えられるプレースホルダーを生成します。<body>
セクションの最後に呼び出す必要があります。これは、EVENT_END_BODY イベントをトリガーし、body の最後に配置される登録済みの HTML コード(例えば JavaScript)で置き換えられるプレースホルダーを生成します。レイアウト内では、2つの事前定義された変数 $this
と $content
にアクセスできます。前者は、通常のビューと同様に、ビューコンポーネントを参照し、後者は、コントローラの render() メソッドを呼び出すことによってレンダリングされるコンテンツビューのレンダリング結果を格納します。
レイアウトで他のデータにアクセスしたい場合は、「ビューでのデータへのアクセス」の項で説明されている pull メソッドを使用する必要があります。コンテンツビューからレイアウトにデータを渡したい場合は、「ビュー間でのデータの共有」の項で説明されているメソッドを使用できます。
「コントローラでのレンダリング」の項で説明したように、コントローラで 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';
// ...
}
モジュールに属するコントローラについては、モジュールの layout プロパティを設定して、これらのコントローラに特定のレイアウトを使用するようにすることもできます。
layout
プロパティは異なるレベル(コントローラ、モジュール、アプリケーション)で設定できるため、Yii はバックグラウンドで2つのステップを実行して、特定のコントローラで使用される実際のレイアウトファイルを決定します。
最初のステップでは、レイアウトの値とコンテキストモジュールを決定します。
null
でない場合は、それをレイアウト値として使用し、コントローラの module をコンテキストモジュールとして使用します。null
の場合は、コントローラのすべての先祖モジュール(アプリケーション自体を含む)を検索し、layout プロパティが null
でない最初のモジュールを見つけます。そのモジュールとその layout の値をコンテキストモジュールと選択されたレイアウト値として使用します。そのようなモジュールが見つからない場合は、レイアウトは適用されないことを意味します。2番目のステップでは、最初のステップで決定されたレイアウト値とコンテキストモジュールに従って、実際のレイアウトファイルを決定します。レイアウト値は、次のいずれかになります。
@app/views/layouts/main
)。/main
):レイアウト値がスラッシュで始まる場合。実際のレイアウトファイルは、アプリケーションの レイアウトパス (デフォルトでは @app/views/layouts
) の下で検索されます。main
):実際のレイアウトファイルは、コンテキストモジュールの レイアウトパス (デフォルトでは モジュールディレクトリ の下の views/layouts
ディレクトリ) の下で検索されます。false
:レイアウトは適用されません。レイアウト値にファイル拡張子が含まれていない場合は、デフォルトの .php
が使用されます。
場合によっては、レイアウトを別のレイアウトにネストしたい場合があります。例えば、Webサイトの異なるセクションで異なるレイアウトを使用したいが、これらのすべてのレイアウトが、全体のHTML5ページ構造を生成する同じ基本レイアウトを共有する場合などです。この目的は、以下のように、子レイアウトで beginContent() と endContent() を呼び出すことで実現できます。
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
...child layout content here...
<?php $this->endContent(); ?>
上記のように、子レイアウトの内容は、beginContent() と endContent() で囲む必要があります。beginContent() に渡されるパラメータは、親レイアウトを指定します。これは、レイアウトファイルまたはエイリアスのいずれかです。
上記のアプローチを使用すると、レイアウトを複数レベルにネストできます。
ブロックを使用すると、ビューの内容を1つの場所で指定し、別の場所に表示することができます。これらは、レイアウトと組み合わせて使用されることがよくあります。例えば、コンテンツビューでブロックを定義し、それをレイアウトに表示することができます。
ブロックを定義するには、beginBlock() と endBlock() を呼び出します。ブロックには、$view->blocks[$blockID]
でアクセスできます。ここで、$blockID
は、ブロックを定義するときにブロックに割り当てる一意の ID を表します。
次の例は、ブロックを使用して、コンテンツビューのレイアウトの特定のパーツをカスタマイズする方法を示しています。
まず、コンテンツビューで、1つまたは複数のブロックを定義します。
...
<?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 またはその子クラスの個々のインスタンスを作成してビューコンポーネントを取得できますが、ほとんどの場合、主に view
アプリケーションコンポーネントを使用します。このコンポーネントは、次の例のように、アプリケーション設定で設定できます。
[
// ...
'components' => [
'view' => [
'class' => 'app\components\View',
],
// ...
],
]
ビューコンポーネントは、次の便利なビュー関連機能を提供します。それぞれの詳細は、別のセクションで説明されています。
Webページを開発する際には、次のマイナーながら便利な機能も頻繁に使用する可能性があります。
すべてのWebページにはタイトルが必要です。通常、タイトルタグは レイアウト に表示されます。ただし、実際には、タイトルはレイアウトではなく、コンテンツビューで決定されることがよくあります。この問題を解決するために、yii\web\View は、コンテンツビューからレイアウトにタイトル情報を渡すための title プロパティを提供しています。
この機能を利用するには、各コンテンツビューで、次のようにページタイトルを設定できます。
<?php
$this->title = 'My page title';
?>
次に、レイアウトで、<head>
セクションに次のコードが含まれていることを確認してください。
<title><?= Html::encode($this->title) ?></title>
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() を複数回呼び出すと、メタタグが同じかどうかに関係なく、複数のメタタグが登録されることに注意してください。
メタタグタイプの単一のインスタンスのみが存在するようにするには、メソッドを呼び出すときに2番目のパラメータとしてキーを指定できます。例えば、次のコードは2つの「description」メタタグを登録しますが、2番目のメタタグのみがレンダリングされます。
$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');
メタタグと同様に、リンクタグは、ファビコンのカスタマイズ、RSSフィードのポイント、OpenIDを別のサーバーに委任するなど、多くのケースで役立ちます。yii\web\View::registerLinkTag() を使用して、メタタグと同様の方法でリンクタグを操作できます。例えば、コンテンツビューで、次のようにリンクタグを登録できます。
$this->registerLinkTag([
'title' => 'Live News for Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'https://yii.dokyumento.jp/rss.xml/',
]);
上記のコードは、次のようになります。
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="https://yii.dokyumento.jp/rss.xml/">
registerMetaTag() と同様に、registerLinkTag() を呼び出すときにキーを指定して、繰り返しリンクタグが生成されないようにすることができます。
ビューコンポーネントは、ビューのレンダリングプロセス中にいくつかのイベントをトリガーします。これらのイベントに応答して、ビューにコンテンツを挿入したり、エンドユーザーに送信する前にレンダリング結果を処理したりできます。
false
に設定して、レンダリングプロセスをキャンセルできます。例えば、次のコードは、ページの本文の最後に現在の日付を挿入します。
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});
静的ページとは、その主なコンテンツが、コントローラからプッシュされる動的データにアクセスする必要がない、ほとんど静的なWebページを指します。
静的ページは、ビューにコードを配置し、コントローラで次のコードを使用することで出力できます。
public function actionAbout()
{
return $this->render('about');
}
Webサイトに多くの静的ページが含まれている場合、同様のコードを何度も繰り返すのは非常に面倒です。この問題を解決するために、コントローラで スタンドアロンアクション と呼ばれる yii\web\ViewAction を導入できます。例えば、
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
という名前のビューを作成すると、次のURLでこのビューを表示できます。
http:///index.php?r=site%2Fpage&view=about
GET
パラメータ view
は、yii\web\ViewAction にどのビューがリクエストされているかを伝えます。その後、アクションは @app/views/site/pages
ディレクトリの下でこのビューを探します。yii\web\ViewAction::$viewPrefix を設定して、これらのビューを検索するディレクトリを変更できます。
ビューは、エンドユーザーが望む形式でモデルを提示する役割を担います。一般的に、ビューは
$_GET
、$_POST
などのリクエストデータに直接アクセスすることは避けるべきです。これはコントローラーの役割です。リクエストデータが必要な場合は、コントローラーによってビューにプッシュされるべきです。ビューをより管理しやすくするために、複雑すぎるビューや、冗長なコードを多く含むビューの作成は避けてください。この目標を達成するために、以下の手法を使用できます。
タイプミスを見つけましたか、またはこのページを改善する必要があると思いますか?
githubで編集する !
ブロックは、タブコンテンツを整理するためにも使用できます。
<?php $this->beginBlock('one'); ?> ...content of tab #1 ... <?php $this->endBlock(); ?> <?php $this->beginBlock('two'); ?> ...content of tab #2 ... <?php $this->endBlock(); ?> ... <?php echo Tabs::Widget([ 'items' => [ [ 'label' => 'Tab #1', 'content' => $this->blocks['one'], 'active' => true, ], [ 'label' => 'Tab #2', 'content' => $this->blocks['two'], ], ... ] ]); ?>
コメントするには、サインアップまたはログインしてください。