structure-views.md 30.7 KB
Newer Older
Qiang Xue committed
1 2
Views
=====
Alexander Makarov committed
3

Qiang Xue committed
4
Views are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
Qiang Xue committed
5 6
They are code responsible for presenting data to end users. In a Web application, views are usually created
in terms of *view templates* which are PHP script files containing mainly HTML code and presentational PHP code.
7
They are managed by the [[yii\web\View|view]] [application component](structure-application-components.md) which provides commonly used methods
Qiang Xue committed
8 9
to facilitate view composition and rendering. For simplicity, we often call view templates or view template files
as views.
Qiang Xue committed
10

Qiang Xue committed
11

12
## Creating Views <a name="creating-views"></a>
Qiang Xue committed
13

Qiang Xue committed
14 15 16 17 18 19 20 21 22
As aforementioned, a view is simply a PHP script mixed with HTML and PHP code. The following is the view
that presents a login form. As you can see, PHP code is used to generate the dynamic content, such as the
page title and the form, while HTML code organizes them into a presentable HTML page.

```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

23 24 25 26
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $model app\models\LoginForm */

Qiang Xue committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
$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(); ?>
```

Within a view, you can access `$this` which refers to the [[yii\web\View|view component]] managing
and rendering this view template.

Alexander Makarov committed
43
Besides `$this`, there may be other predefined variables in a view, such as `$model` in the above
Qiang Xue committed
44
example. These variables represent the data that are *pushed* into the view by [controllers](structure-controllers.md)
45
or other objects which trigger the [view rendering](#rendering-views).
Qiang Xue committed
46 47

> Tip: The predefined variables are listed in a comment block at beginning of a view so that they can
48
  be recognized by IDEs. It is also a good way of documenting your views.
Qiang Xue committed
49 50


51
### Security <a name="security"></a>
Qiang Xue committed
52

53 54 55
When creating views that generate HTML pages, it is important that you encode and/or filter the data coming
from end users before presenting them. Otherwise, your application may be subject to
[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) attacks.
Qiang Xue committed
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
To display a plain text, encode it first by calling [[yii\helpers\Html::encode()]]. For example, the following code
encodes the user name before displaying it:

```php
<?php
use yii\helpers\Html;
?>

<div class="username">
    <?= Html::encode($user->name) ?>
</div>
```

To display HTML content, use [[yii\helpers\HtmlPurifier]] to filter the content first. For example, the following
code filters the post content before displaying it:

```php
<?php
use yii\helpers\HtmlPurifier;
?>

<div class="post">
    <?= HtmlPurifier::process($post->text) ?>
</div>
```

> Tip: While HTMLPurifier does excellent job in making output safe, it is not fast. You should consider
  [caching](caching-overview.md) the filtering result if your application requires high performance.
Qiang Xue committed
85

86 87

### Organizing Views <a name="organizing-views"></a>
Qiang Xue committed
88 89 90

Like [controllers](structure-controllers.md) and [models](structure-models.md), there are conventions to organize views.

91
* For views rendered by a controller, they should be put under the directory `@app/views/ControllerID` by default,
92 93
  where `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if
  the controller class is `PostController`, the directory would be `@app/views/post`; If it is `PostCommentController`,
Qiang Xue committed
94 95 96 97 98 99 100 101 102 103
  the directory would be `@app/views/post-comment`. In case the controller belongs to a module, the directory
  would be `views/ControllerID` under the [[yii\base\Module::basePath|module directory]].
* For views rendered in a [widget](structure-widgets.md), they should be put under the `WidgetPath/views` directory by
  default, where `WidgetPath` stands for the directory containing the widget class file.
* For views rendered by other objects, it is recommended that you follow the similar convention as that for widgets.

You may customize these default view directories by overriding the [[yii\base\ViewContextInterface::getViewPath()]]
method of controllers or widgets.


104
## Rendering Views <a name="rendering-views"></a>
Qiang Xue committed
105 106

You can render views in [controllers](structure-controllers.md), [widgets](structure-widgets.md), or any
Qiang Xue committed
107
other places by calling view rendering methods. These methods share a similar signature shown as follows,
Qiang Xue committed
108 109 110 111

```
/**
 * @param string $view view name or file path, depending on the actual rendering method
Qiang Xue committed
112 113
 * @param array $params the data to be passed to the view
 * @return string rendering result
Qiang Xue committed
114 115 116 117 118
 */
methodName($view, $params = [])
```


119
### Rendering in Controllers <a name="rendering-in-controllers"></a>
Qiang Xue committed
120

Qiang Xue committed
121
Within [controllers](structure-controllers.md), you may call the following controller methods to render views:
Qiang Xue committed
122 123 124 125

* [[yii\base\Controller::render()|render()]]: renders a [named view](#named-views) and applies a [layout](#layouts)
  to the rendering result.
* [[yii\base\Controller::renderPartial()|renderPartial()]]: renders a [named view](#named-views) without any layout.
Qiang Xue committed
126 127
* [[yii\web\Controller::renderAjax()|renderAjax()]]: renders a [named view](#named-views) without any layout,
  and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests.
Qiang Xue committed
128 129
* [[yii\base\Controller::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
  [alias](concept-aliases.md).
130 131
* [[yii\base\Controller::renderContent()|renderContent()]]: renders a static string by embedding it into
  the currently applicable [layout](#layouts). This method is available since version 2.0.1.
Qiang Xue committed
132 133

For example,
Qiang Xue committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

```php
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;
        }

Qiang Xue committed
152
        // renders a view named "view" and applies a layout to it
Qiang Xue committed
153 154 155 156 157 158 159 160
        return $this->render('view', [
            'model' => $model,
        ]);
    }
}
```


161
### Rendering in Widgets <a name="rendering-in-widgets"></a>
Qiang Xue committed
162 163 164 165 166 167

Within [widgets](structure-widgets.md), you may call the following widget methods to render views.

* [[yii\base\Widget::render()|render()]]: renders a [named view](#named-views).
* [[yii\base\Widget::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
  [alias](concept-aliases.md).
Qiang Xue committed
168

Qiang Xue committed
169
For example,
Qiang Xue committed
170

Qiang Xue committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
```php
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,
        ]);
    }
}
```
Qiang Xue committed
190 191


192
### Rendering in Views <a name="rendering-in-views"></a>
Qiang Xue committed
193

194
You can render a view within another view by calling one of the following methods provided by the [[yii\base\View|view component]]:
Qiang Xue committed
195

Qiang Xue committed
196
* [[yii\base\View::render()|render()]]: renders a [named view](#named-views).
Qiang Xue committed
197 198
* [[yii\web\View::renderAjax()|renderAjax()]]: renders a [named view](#named-views) and injects all registered
  JS/CSS scripts and files. It is usually used in response to AJAX Web requests.
Qiang Xue committed
199 200
* [[yii\base\View::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
  [alias](concept-aliases.md).
Qiang Xue committed
201

202 203
For example, the following code in a view renders the `_overview.php` view file which is in the same directory
as the view being currently rendered. Remember that `$this` in a view refers to the [[yii\base\View|view]] component:
Qiang Xue committed
204 205

```php
206
<?= $this->render('_overview') ?>
Qiang Xue committed
207 208
```

209 210 211 212 213

### Rendering in Other Places <a name="rendering-in-other-places"></a>

In any place, you can get access to the [[yii\base\View|view]] application component by the expression
`Yii::$app->view` and then call its aforementioned methods to render a view. For example,
Qiang Xue committed
214 215

```php
216 217
// displays the view file "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
Qiang Xue committed
218 219 220
```


221
### Named Views <a name="named-views"></a>
Qiang Xue committed
222 223 224 225 226 227 228 229 230 231 232

When you render a view, you can specify the view using either a view name or a view file path/alias. In most cases,
you would use the former because it is more concise and flexible. We call views specified using names as *named views*.

A view name is resolved into the corresponding view file path according to the following rules:

* A view name may omit the file extension name. In this case, `.php` will be used as the extension. For example,
  the view name `about` corresponds to the file name `about.php`.
* If the view name starts with double slashes `//`, the corresponding view file path would be `@app/views/ViewName`.
  That is, the view is looked for under the [[yii\base\Application::viewPath|application's view path]].
  For example, `//site/about` will be resolved into `@app/views/site/about.php`.
Qiang Xue committed
233 234 235
* If the view name starts with a single slash `/`, the view file path is formed by prefixing the view name
  with the [[yii\base\Module::viewPath|view path]] of the currently active [module](structure-modules.md).
  If there is no active module, `@app/views/ViewName` will be used. For example, `/user/create` will be resolved into
Qiang Xue committed
236 237 238 239 240 241 242
  `@app/modules/user/views/user/create.php`, if the currently active module is `user`. If there is no active module,
  the view file path would be `@app/views/user/create.php`.
* If the view is rendered with a [[yii\base\View::context|context]] and the context implements [[yii\base\ViewContextInterface]],
  the view file path is formed by prefixing the [[yii\base\ViewContextInterface::getViewPath()|view path]] of the
  context to the view name. This mainly applies to the views rendered within controllers and widgets. For example,
  `site/about` will be resolved into `@app/views/site/about.php` if the context is the controller `SiteController`.
* If a view is rendered within another view, the directory containing the other view file will be prefixed to
243
  the new view name to form the actual view file path. For example, `item` will be resolved into `@app/views/post/item.php`
Qiang Xue committed
244 245
  if it is being rendered in the view `@app/views/post/index.php`.

246 247 248
According to the above rules, calling `$this->render('view')` in a controller `app\controllers\PostController` will
actually render the view file `@app/views/post/view.php`, while calling `$this->render('_overview')` in that view
will render the view file `@app/views/post/_overview.php`.
Qiang Xue committed
249 250


251
### Accessing Data in Views <a name="accessing-data-in-views"></a>
Qiang Xue committed
252

Qiang Xue committed
253
There are two approaches to access data within a view: push and pull.
Qiang Xue committed
254

Qiang Xue committed
255
By passing the data as the second parameter to the view rendering methods, you are using the push approach.
256
The data should be represented as an array of name-value pairs. When the view is being rendered, the PHP
Qiang Xue committed
257 258 259
`extract()` function will be called on this array so that the array is extracted into variables in the view.
For example, the following view rendering code in a controller will push two variables to the `report` view:
`$foo = 1` and `$bar = 2`.
Qiang Xue committed
260

Qiang Xue committed
261 262 263 264 265 266
```php
echo $this->render('report', [
    'foo' => 1,
    'bar' => 2,
]);
```
Qiang Xue committed
267

268
The pull approach actively retrieves data from the [[yii\base\View|view component]] or other objects accessible
269
in views (e.g. `Yii::$app`). Using the code below as an example, within the view you can get the controller object
270 271
by the expression `$this->context`. And as a result, it is possible for you to access any properties or methods
of the controller in the `report` view, such as the controller ID shown in the following:
Qiang Xue committed
272

Qiang Xue committed
273 274 275 276 277
```php
The controller ID is: <?= $this->context->id ?>
?>
```

278
The push approach is usually the preferred way of accessing data in views, because it makes views less dependent
Qiang Xue committed
279
on context objects. Its drawback is that you need to manually build the data array all the time, which could
Bazilio committed
280
become tedious and error prone if a view is shared and rendered in different places.
Qiang Xue committed
281

Qiang Xue committed
282

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
### Sharing Data among Views <a name="sharing-data-among-views"></a>

The [[yii\base\View|view component]] provides the [[yii\base\View::params|params]] property that you can use
to share data among views.

For example, in an `about` view, you can have the following code which specifies the current segment of the
breadcrumbs.

```php
$this->params['breadcrumbs'][] = 'About Us';
```

Then, in the [layout](#layouts) file, which is also a view, you can display the breadcrumbs using the data
passed along [[yii\base\View::params|params]]:

```php
<?= yii\widgets\Breadcrumbs::widget([
    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
```


## Layouts <a name="layouts"></a>
Qiang Xue committed
306 307 308 309 310

Layouts are a special type of views that represent the common parts of multiple views. For example, the pages
for most Web applications share the same page header and footer. While you can repeat the same page header and footer
in every view, a better way is to do this once in a layout and embed the rendering result of a content view at
an appropriate place in the layout.
Alexander Makarov committed
311 312


313
### Creating Layouts <a name="creating-layouts"></a>
Alexander Makarov committed
314

315 316 317 318 319 320 321 322
Because layouts are also views, they can be created in the similar way as normal views. By default, layouts
are stored in the directory `@app/views/layouts`. For layouts used within a [module](structure-modules.md),
they should be stored in the `views/layouts` directory under the [[yii\base\Module::basePath|module directory]].
You may customize the default layout directory by configuring the [[yii\base\Module::layoutPath]] property of
the application or modules.

The following example shows how a layout looks like. Note that for illustrative purpose, we have greatly simplified
the code in the layout. In practice, you may want to add more content to it, such as head tags, main menu, etc.
Alexander Makarov committed
323 324

```php
Qiang Xue committed
325 326
<?php
use yii\helpers\Html;
327 328 329

/* @var $this yii\web\View */
/* @var $content string */
Qiang Xue committed
330 331 332
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
333
<html lang="en">
Qiang Xue committed
334
<head>
335 336
    <meta charset="UTF-8"/>
    <?= Html::csrfMetaTags() ?>
Qiang Xue committed
337 338 339 340 341
    <title><?= Html::encode($this->title) ?></title>
    <?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
342 343 344
    <header>My Company</header>
    <?= $content ?>
    <footer>&copy; 2014 by My Company</footer>
Qiang Xue committed
345 346 347 348
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
Alexander Makarov committed
349 350
```

351 352 353
As you can see, the layout generates the HTML tags that are common to all pages. Within the `<body>` section,
the layout echoes the `$content` variable which represents the rendering result of content views and is pushed
into the layout when [[yii\base\Controller::render()]] is called.
Alexander Makarov committed
354

355 356 357
Most layouts should call the following methods like shown in the above code. These methods mainly trigger events
about the rendering process so that scripts and tags registered in other places can be properly injected into
the places where these methods are called.
358

359 360 361 362 363 364 365
- [[yii\base\View::beginPage()|beginPage()]]: This method should be called at the very beginning of the layout.
  It triggers the [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] event which indicates the beginning of a page.
- [[yii\base\View::endPage()|endPage()]]: This method should be called at the end of the layout.
  It triggers the [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] event which indicates the end of a page.
- [[yii\web\View::head()|head()]]: This method should be called within the `<head>` section of an HTML page.
  It generates a placeholder which will be replaced with the registered head HTML code (e.g. link tags, meta tags)
  when a page finishes rendering.
marsuboss committed
366
- [[yii\web\View::beginBody()|beginBody()]]: This method should be called at the beginning of the `<body>` section.
367 368
  It triggers the [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] event and generates a placeholder which will
  be replaced by the registered HTML code (e.g. JavaScript) targeted at the body begin position.
marsuboss committed
369
- [[yii\web\View::endBody()|endBody()]]: This method should be called at the end of the `<body>` section.
370 371
  It triggers the [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] event and generates a placeholder which will
  be replaced by the registered HTML code (e.g. JavaScript) targeted at the body end position.
Alexander Makarov committed
372 373


374
### Accessing Data in Layouts <a name="accessing-data-in-layouts"></a>
Alexander Makarov committed
375

376 377 378
Within a layout, you have access to two predefined variables: `$this` and `$content`. The former refers to
the [[yii\base\View|view]] component, like in normal views, while the latter contains the rendering result of a content
view which is rendered by calling the [[yii\base\Controller::render()|render()]] method in controllers.
379

380 381 382
If you want to access other data in layouts, you have to use the pull method as described in
the [Accessing Data in Views](#accessing-data-in-views) subsection. If you want to pass data from a content view
to a layout, you may use the method described in the [Sharing Data among Views](#sharing-data-among-views) subsection.
Qiang Xue committed
383 384


385
### Using Layouts <a name="using-layouts"></a>
Alexander Makarov committed
386

387 388 389
As described in the [Rendering in Controllers](#rendering-in-controllers) subsection, when you render a view
by calling the [[yii\base\Controller::render()|render()]] method in a controller, a layout will be applied
to the rendering result. By default, the layout `@app/views/layouts/main.php` will be used. 
390

391 392 393 394 395 396 397 398
You may use a different layout by configuring either [[yii\base\Application::layout]] or [[yii\base\Controller::layout]].
The former governs the layout used by all controllers, while the latter overrides the former for individual controllers.
For example, the following code makes the `post` controller to use `@app/views/layouts/post.php` as the layout
when rendering its views. Other controllers, assuming their `layout` property is untouched, will still use the default
`@app/views/layouts/main.php` as the layout.
 
```php
namespace app\controllers;
399

400
use yii\web\Controller;
401

402 403 404 405 406 407 408
class PostController extends Controller
{
    public $layout = 'post';
    
    // ...
}
```
409

410 411
For controllers belonging to a module, you may also configure the module's [[yii\base\Module::layout|layout]] property to
use a particular layout for these controllers. 
412

413 414
Because the `layout` property may be configured at different levels (controllers, modules, application),
behind the scene Yii takes two steps to determine what is the actual layout file being used for a particular controller.
Qiang Xue committed
415

416
In the first step, it determines the layout value and the context module:
Qiang Xue committed
417

418 419
- If the [[yii\base\Controller::layout]] property of the controller is not null, use it as the layout value and
  the [[yii\base\Controller::module|module]] of the controller as the context module.
420
- If [[yii\base\Controller::layout|layout]] is null, search through all ancestor modules (including the application itself) of the controller and 
421 422 423 424 425 426
  find the first module whose [[yii\base\Module::layout|layout]] property is not null. Use that module and
  its [[yii\base\Module::layout|layout]] value as the context module and the chosen layout value.
  If such a module cannot be found, it means no layout will be applied.
  
In the second step, it determines the actual layout file according to the layout value and the context module
determined in the first step. The layout value can be:
Qiang Xue committed
427

428 429 430 431 432 433 434 435
- a path alias (e.g. `@app/views/layouts/main`).
- an absolute path (e.g. `/main`): the layout value starts with a slash. The actual layout file will be
  looked for under the application's [[yii\base\Application::layoutPath|layout path]] which defaults to
  `@app/views/layouts`.
- a relative path (e.g. `main`): the actual layout file will be looked for under the context module's
  [[yii\base\Module::layoutPath|layout path]] which defaults to the `views/layouts` directory under the
  [[yii\base\Module::basePath|module directory]].
- the boolean value `false`: no layout will be applied.
Qiang Xue committed
436

437
If the layout value does not contain a file extension, it will use the default one `.php`.
Qiang Xue committed
438 439


440
### Nested Layouts <a name="nested-layouts"></a>
Qiang Xue committed
441

442 443
Sometimes you may want to nest one layout in another. For example, in different sections of a Web site, you
want to use different layouts, while all these layouts share the same basic layout that generates the overall
Bazilio committed
444
HTML5 page structure. You can achieve this goal by calling [[yii\base\View::beginContent()|beginContent()]] and
445
[[yii\base\View::endContent()|endContent()]] in the child layouts like the following:
Qiang Xue committed
446

447 448
```php
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
Qiang Xue committed
449

450
...child layout content here...
Qiang Xue committed
451

452
<?php $this->endContent(); ?>
453 454
```

Bazilio committed
455
As shown above, the child layout content should be enclosed within [[yii\base\View::beginContent()|beginContent()]] and
456 457
[[yii\base\View::endContent()|endContent()]]. The parameter passed to [[yii\base\View::beginContent()|beginContent()]]
specifies what is the parent layout. It can be either a layout file or alias.
458

459
Using the above approach, you can nest layouts in more than one levels.
460

Alexander Makarov committed
461

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
### Using Blocks <a name="using-blocks"></a>

Blocks allow you to specify the view content in one place while displaying it in another. They are often used together
with layouts. For example, you can define a block in a content view and display it in the layout.

You call [[yii\base\View::beginBlock()|beginBlock()]] and [[yii\base\View::endBlock()|endBlock()]] to define a block.
The block can then be accessed via `$view->blocks[$blockID]`, where `$blockID` stands for a unique ID that you assign
to the block when defining it.

The following example shows how you can use blocks to customize specific parts of a layout in a content view.

First, in a content view, define one or multiple blocks:

```php
...

<?php $this->beginBlock('block1'); ?>

...content of block1...

<?php $this->endBlock(); ?>

...

<?php $this->beginBlock('block3'); ?>

...content of block3...

<?php $this->endBlock(); ?>
```

Then, in the layout view, render the blocks if they are available, or display some default content if a block is
not defined.

```php
...
<?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; ?>
...
```


523
## Using View Components <a name="using-view-components"></a>
Alexander Makarov committed
524

525 526 527 528
[[yii\base\View|View components]] provides many view-related features. While you can get view components
by creating individual instances of [[yii\base\View]] or its child class, in most cases you will mainly use
the `view` application component. You can configure this component in [application configurations](structure-applications.md#application-configurations)
like the following:
Alexander Makarov committed
529 530

```php
531 532 533 534 535 536 537 538 539
[
    // ...
    'components' => [
        'view' => [
            'class' => 'app\components\View',
        ],
        // ...
    ],
]
Qiang Xue committed
540
```
Alexander Makarov committed
541

542
View components provide the following useful view-related features, each described in more details in a separate section:
Qiang Xue committed
543

544 545 546 547 548 549
* [theming](output-theming.md): allows you to develop and change the theme for your Web site.
* [fragment caching](caching-fragment.md): allows you to cache a fragment within a Web page.
* [client script handling](output-client-scripts.md): supports CSS and JavaScript registration and rendering.
* [asset bundle handling](structure-assets.md): supports registering and rendering of [asset bundles](structure-assets.md).
* [alternative template engines](tutorial-template-engines.md): allows you to use other template engines, such as
  [Twig](http://twig.sensiolabs.org/), [Smarty](http://www.smarty.net/).
Qiang Xue committed
550

551
You may also frequently use the following minor yet useful features when you are developing Web pages.
Qiang Xue committed
552 553


554
### Setting Page Titles <a name="setting-page-titles"></a>
Qiang Xue committed
555

556
Every Web page should have a title. Normally the title tag is being displayed in a [layout](#layouts). However, in practice
557 558
the title is often determined in content views rather than layouts. To solve this problem, [[yii\web\View]] provides
the [[yii\web\View::title|title]] property for you to pass the title information from content views to layouts.
Qiang Xue committed
559

560
To make use of this feature, in each content view, you can set the page title like the following:
Qiang Xue committed
561 562

```php
563 564 565
<?php
$this->title = 'My page title';
?>
Alexander Makarov committed
566 567
```

568
Then in the layout, make sure you have the following code in the `<head>` section:
Alexander Makarov committed
569 570

```php
571
<title><?= Html::encode($this->title) ?></title>
Alexander Makarov committed
572 573
```

Qiang Xue committed
574

575
### Registering Meta Tags <a name="registering-meta-tags"></a>
Qiang Xue committed
576

577 578
Web pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags
appear in the `<head>` section and are usually generated in layouts.
Alexander Makarov committed
579

580 581
If you want to specify what meta tags to generate in content views, you can call [[yii\web\View::registerMetaTag()]]
in a content view, like the following:
Alexander Makarov committed
582 583

```php
584 585 586
<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>
Alexander Makarov committed
587 588
```

589
The above code will register a "keywords" meta tag with the view component. The registered meta tag is
590 591
rendered after the layout finishes rendering. The following HTML code will be generated and inserted
at the place where you call [[yii\web\View::head()]] in the layout:
Alexander Makarov committed
592 593

```php
594
<meta name="keywords" content="yii, framework, php">
Alexander Makarov committed
595 596
```

597 598
Note that if you call [[yii\web\View::registerMetaTag()]] multiple times, it will register multiple meta tags,
regardless whether the meta tags are the same or not.
Alexander Makarov committed
599

600
To make sure there is only a single instance of a meta tag type, you can specify a key as a second parameter when calling the method.
601
For example, the following code registers two "description" meta tags. However, only the second one will be rendered.
Alexander Makarov committed
602 603

```html
604 605
$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');
Alexander Makarov committed
606 607 608
```


609
### Registering Link Tags <a name="registering-link-tags"></a>
Alexander Makarov committed
610

611
Like [meta tags](#registering-meta-tags), link tags are useful in many cases, such as customizing favicon, pointing to
612 613
RSS feed or delegating OpenID to another server. You can work with link tags in the similar way as meta tags
by using [[yii\web\View::registerLinkTag()]]. For example, in a content view, you can register a link tag like follows,
Alexander Makarov committed
614 615

```php
Alexander Makarov committed
616
$this->registerLinkTag([
617
    'title' => 'Live News for Yii',
618 619 620
    'rel' => 'alternate',
    'type' => 'application/rss+xml',
    'href' => 'http://www.yiiframework.com/rss.xml/',
Alexander Makarov committed
621
]);
Alexander Makarov committed
622 623 624 625 626
```

The code above will result in

```html
627
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml/">
Alexander Makarov committed
628 629
```

630
Similar as [[yii\web\View::registerMetaTag()|registerMetaTags()]], you can specify a key when calling
631
[[yii\web\View::registerLinkTag()|registerLinkTag()]] to avoid generating repeated link tags.
Alexander Makarov committed
632 633


634
## View Events <a name="view-events"></a>
Alexander Makarov committed
635

636 637
[[yii\base\View|View components]] trigger several events during the view rendering process. You may respond
to these events to inject content into views or process the rendering results before they are sent to end users.
Alexander Makarov committed
638

639 640
- [[yii\base\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: triggered at the beginning of rendering a file
  in a controller. Handlers of this event may set [[yii\base\ViewEvent::isValid]] to be false to cancel the rendering process.
641
- [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: triggered after rendering a file by the call of [[yii\base\View::afterRender()]].
642 643 644 645 646 647
  Handlers of this event may obtain the rendering result through [[yii\base\ViewEvent::output]] and may modify
  this property to change the rendering result.
- [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts.
- [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]]: triggered by the call of [[yii\base\View::endPage()]] in layouts.
- [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: triggered by the call of [[yii\web\View::beginBody()]] in layouts.
- [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]]: triggered by the call of [[yii\web\View::endBody()]] in layouts.
Alexander Makarov committed
648

649
For example, the following code injects the current date at the end of the page body:
Alexander Makarov committed
650 651

```php
652 653 654
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
    echo date('Y-m-d');
});
Alexander Makarov committed
655 656 657
```


658
## Rendering Static Pages <a name="rendering-static-pages"></a>
Alexander Makarov committed
659

660 661
Static pages refer to those Web pages whose main content are mostly static without the need of accessing
dynamic data pushed from controllers.
662

663
You can output static pages by putting their code in the view, and then using the code like the following in a controller:
664 665

```php
666 667 668 669
public function actionAbout()
{
    return $this->render('about');
}
670 671
```

672 673 674
If a Web site contains many static pages, it would be very tedious repeating the similar code many times.
To solve this problem, you may introduce a [standalone action](structure-controllers.md#standalone-actions)
called [[yii\web\ViewAction]] in a controller. For example,
Alexander Makarov committed
675 676

```php
677
namespace app\controllers;
Alexander Makarov committed
678

679
use yii\web\Controller;
Alexander Makarov committed
680

681 682 683 684 685 686 687 688 689
class SiteController extends Controller
{
    public function actions()
    {
        return [
            'page' => [
                'class' => 'yii\web\ViewAction',
            ],
        ];
690
    }
691
}
Alexander Makarov committed
692 693
```

694 695
Now if you create a view named `about` under the directory `@app/views/site/pages`, you will be able to
display this view by the following URL:
Alexander Makarov committed
696 697

```
698
http://localhost/index.php?r=site/page&view=about
699
```
Qiang Xue committed
700

701 702 703
The `GET` parameter `view` tells [[yii\web\ViewAction]] which view is requested. The action will then look
for this view under the directory `@app/views/site/pages`. You may configure [[yii\web\ViewAction::viewPrefix]]
to change the directory for searching these views.
Qiang Xue committed
704 705


706
## Best Practices <a name="best-practices"></a>
Qiang Xue committed
707

708
Views are responsible for presenting models in the format that end users desire. In general, views
Qiang Xue committed
709

710 711 712 713 714
* should mainly contain presentational code, such as HTML, and simple PHP code to traverse, format and render data.
* should not contain code that performs DB queries. Such code should be done in models.
* should avoid direct access to request data, such as `$_GET`, `$_POST`. This belongs to controllers.
  If request data is needed, they should be pushed into views by controllers.
* may read model properties, but should not modify them.
Qiang Xue committed
715

716 717
To make views more manageable, avoid creating views that are too complex or contain too much redundant code.
You may use the following techniques to achieve this goal:
Qiang Xue committed
718

719 720 721 722 723
* use [layouts](#layouts) to represent common presentational sections (e.g. page header, footer).
* divide a complicated view into several smaller ones. The smaller views can be rendered and assembled into a bigger
  one using the rendering methods that we have described.
* create and use [widgets](structure-widgets.md) as building blocks of views.
* create and use helper classes to transform and format data in views.
Qiang Xue committed
724