Виды - это часть [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры, это код, который отвечает за представление данных
конечным пользователям. В веб приложениях виды создаются обычно в виде *видов - шаблонов*, которые суть PHP скрипты, в основном содержащие HTML код
и код PHP, отвечающий за представление и внешний вид. Виды управляются компонентом приложения [[yii\web\View|view]], который содержит часто используемые
методы для упорядочивания видов и их рендеринга. Для упрощения, мы будем называть виды - шаблоны просто видами.
Как мы упоминали ранее, вид - это просто PHP скрипт, состоящий из PHP и HTML кодa. В примере ниже - вид, который представляет форму авторизации.
Как видите, PHP код здесь генерирует динамический контент, как, например, заголовок страницы и саму форму, тогда как HTML организует полученные данные в готовую html страницу.
```php
<?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 = 'Вход';
?>
<h1><?= Html::encode($this->title) ?></h1>
<p>Пожалуйста, заполните следующие поля для входа на сайт:</p>
Внутри вида, вы можете использовать `$this`, которое представляет собой [[yii\web\View|компонент вид]], управляющий этим шаблоном и обеспечивающий
его рендеринг.
Кроме `$this`, в виде могут быть доступны другие переменные, такие как `$form` и `$model` из примера выше. Эти переменные представляют собой данные, которые передаются в вид [контроллерами](structure-controllers.md) или другими объектами, которые вызывают [рендеринг вида](#rendering-views).
> Совет: Переданные переменные могут быть перечислены в блоке комментария в начале скрипта, чтобы их смогли распознать IDE. К тому же, это хороший способ добавления документации в вид.
При создании видов, которые генерируют HTML страницы, важно кодировать и/или фильтровать данные, которые приходят от пользователей перед тем как их показывать. В противном случае ваше приложение может стать жертвой атаки типа [межсайтовый скриптинг](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3)
Чтобы показать обычный текст, сначала кодируйте его с помощью [[yii\helpers\Html::encode()]]. В примере ниже имя пользователя кодируется перед выводом:
```php
<?php
use yii\helpers\Html;
?>
<divclass="username">
<?= Html::encode($user->name) ?>
</div>
```
Чтобы показать HTML содержимое, используйте [[yii\helpers\HtmlPurifier]] для того, чтобы отфильтровать потенциально опасное содержимое. В примере ниже содержимое поста фильтруется перед показом:
```php
<?php
use yii\helpers\HtmlPurifier;
?>
<divclass="post">
<?= HtmlPurifier::process($post->text) ?>
</div>
```
> Совет: Несмотря на то, что HTMLPurifier отлично справляется с тем, чтобы сделать вывод безопасным, работает он довольно медленно. Если от приложения требуется высокая производительность, рассмотрите возможность [кэширования](caching-overview.md) отфитрованного результата
* Виды, которые рендерятся из контроллера, по умолчанию должны располагаться в папке `@app/views/ControllerID`, где `ControllerID` это [ID контроллера](structure-controllers.md#routes) . Например, если класс контроллера - `PostController`, то папка будет `@app/views/post`; если контроллер - `PostCommentController`, то папка будет `@app/views/post-comment`. В случае, если контроллер принадлежит модулю, папка будет `views/ControllerID` в [[yii\base\Module::basePath|подпапке модуля]].
* Виды, которые рендерятся из виджетов, должны располагаться в `ПутьВиджета/views`, где `ПутьВиджета` - это папка, которая содержит класс виджета.
* С видами, которые рендерятся из других объектов рекомендуется поступать по той же схеме, что и с видами виджетов.
Вы можете рендерить виды в [контроллерах](structure-controllers.md), [widgets](structure-widgets.md), или из любого другого места, вызывая методы рендеринга видов. Методы вызываются приблизительно так, как это показано в примере ниже,
```
/**
*@param string $view название вида или путь файла, в зависимости от того, какой метод рендеринга используется
*@param array $params данные, которые передаются виду
* [[yii\web\View::renderAjax()|renderAjax()]]: рендерит [именованный вид](#named-views) и добавляет зарегистрированные JS/CSS скрипты и стили. Обычно используется для рендеринга результата AJAX запроса.
* [[yii\base\View::renderFile()|renderFile()]]: рендерит вид, заданный как путь к файлу или
[алиас](concept-aliases.md).
Например, следующий код рендерит `_overview.php` файл вида, который находится в той же папке что и вид, который рендерится в текущий момент. Помните, что `$this` в виде - это [[yii\base\View|компонент вида]] (а не контроллер, как это было в Yii1):
При рендеринге вида, вы можете указать нужный вид, используя как имя вида, так и путь к файлу/алиас. В большинстве случаев вы будете использовать первый вариант, т.к. он более нагляден и гибок. Мы называем виды, которые были вызваны с помощью сокращенного имени *именованные виды*.
Имя вида преобразуется в соответствующий ему путь файла в соответствии со следующими правилами:
* Имя вида можно указывать без расширения. В таком случае в качестве расширения будет использоваться `.php`. К примеру, имя вида `about` соответствует файлу `about.php`.
* Если имя вида начинается с двойного слеша `//`, соответствующий ему путь будет `@app/views/ViewName`.
Т.е. вид будет искаться в [[yii\base\Application::viewPath|папке видов приложения по умолчанию]]. Например, `//site/about` будет преобразован в `@app/views/site/about.php`.
* Если имя вида начинается с одинарного слеша `/`, то вид будет искаться в [[yii\base\Module::viewPath|папке видов по умолчанию]] текущего [модуля](structure-modules.md) . Если активного модуля на данный момент нет, будет использована папка видов приложения по умолчанию, т.е. вид будет искаться в `@app/views`, как в одном из примеров выше.
* Если вид рендеринтся с помощью [[yii\base\View::context|контекста]] и контекст реализует интерфейс [[yii\base\ViewContextInterface]], путь к виду образуется путем присоединения [[yii\base\ViewContextInterface::getViewPath()|пути видов]] контекста к имени вида. В основном это применимо к видам, которые рендерятся из контроллеров и виджетов. Например,
* Если вид рендерится из другого вида, папка, в которой находится текущий вид будет добавлена к пути вложенного вида. Например, `item` будет преобразован в `@app/views/post/item`
если он рендерится из вида `@app/views/post/index.php`.
В соответствии с вышесказанным, вызов `$this->render('view')` в контроллере `app\controllers\PostController` будет рендерить файл `@app/views/post/view.php`, а вызов `$this->render('_overview')` в этом виде будет рендерить файл `@app/views/post/_overview.php`.
Данные должны быть представлены как обычный массив: ключ-значение. При рендеринге вида, php вызывает встроенную функцию PHP `extract()` на переданном массиве, чтобы переменные из массива "распаковались" в переменные вида. Например, следующий код в контроллере передаст две переменные виду `report` :
`$foo = 1` и `$bar = 2`.
```php
echo $this->render('report', [
'foo' => 1,
'bar' => 2,
]);
```
Другой подход, подход контекстного доступа, извлекает данные из [[yii\base\View|компонента вида]] или других объектов, доступных в виде (например через глобальный контейнер `Yii::$app`). Внутри вида вы можете вызывать объект контроллера таким образом: `$this->context` (см пример снизу), и, таким образом, получить доступ к его свойствам и методам, например, как указано в примере, вы можете получить ID контроллера:
```php
ID контроллера: <?= $this->context->id ?>
```
Явная передача данных в вид обычно более предпочтительна, т.к. она делает виды независимыми от контекста. Однако, у нее есть недостаток - необходимость каждый раз вручную строить массив данных, что может быть довольно утомительно и привести к ошибкам, если вид рендерится в разных местах.
Например, в виде `about` вы можете указать текущий сегмент хлебных крошек с помощью следующего кода.
```php
$this->params['breadcrumbs'][] = 'О нас';
```
Затем, в [шаблоне](#layouts), который также является видом, вы можете отобразить хлебные крошки используя данные, переданные через [[yii\base\View::params|params]].
Шаблоны - особый тип видов, которые представляют собой общие части разных видов. Например, у большинства страниц веб приложений одинаковые верх и низ (хедер и футер). Можно, конечно, указать их и в каждом виде, однако лучше сделать это один раз, в шаблоне, и затем, при рендеринге, включать уже отрендеренный вид в заданное место шаблона.
Поскольку шаблоны это виды, их можно создавать точно так же, как и обычные виды. По умолчанию шаблоны хранятся в папке `@app/views/layouts`. Шаблоны, которые используются в конкретном [модуле](structure-modules.md), хранятся в подпапке `views/layouts` [[yii\base\Module::basePath|папки модуля]]. Вы можете изменить папку шаблонов по умолчанию, используя свойство [[yii\base\Module::layoutPath]] приложения или модулей.
Пример ниже показывает как выглядит шаблон. Для лучшего понимания мы сильно упростили код шаблона. На практике, однако, в нем часто содержится больше кода, например, тэги `<head>`, главное меню и т.д.
Как видите, шаблон генерирует HTML тэги, которые присутствуют на всех страницах. Внутри секции `<body>`, шаблон выводит переменную `$content`, которая содержит результат рендеринга видов контента, который передается в шаблон, при работе метода [[yii\base\Controller::render()]].
Большинство шаблонов вызывают методы, аналогично тому, как это сделано в примере выше, чтобы скрипты и тэги, зарегистированные в других местах приложения могли быть правильно отображены в местах вызова (например, в шаблоне).
- [[yii\base\View::beginPage()|beginPage()]]: Этот метод нужно вызывать в самом начале шаблона.
- [[yii\base\View::endPage()|endPage()]]: Этот метод нужно вызывать в конце страницы.
Он вызывает событие [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] . Оно указывает на обработку конца страницы.
- [[yii\web\View::head()|head()]]: Этот метод нужно вызывать в `<head>` секции страницы html.
Он генерирует метку, которая будет заменена зарегистрированным ранее кодом HTML (тэги `link`, мета тэги), когда рендеринг страницы будет завершен.
- [[yii\web\View::beginBody()|beginBody()]]: Этот метод нужно вызывать в начале секции `<body>`.
Он вызывает событие [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] и генерирует метку, которая будет заменена зарегистрированным HTML кодом (например, Javascript'ом), который нужно разместить в начале `<body>` страницы.
- [[yii\web\View::endBody()|endBody()]]: Этот метод нужно вызывать в конце секции `<body>`.
Он вызывает событие [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] и генерирует метку, которая будет заменена зарегистрированным HTML кодом (например, Javascript'ом), который нужно разместить в конце `<body>` страницы.
Внутри шаблона, у вас есть доступ к двум предопределенным переменным: `$this` и `$content`. Первая представляет собой
[[yii\base\View|вид]] компонент, как и в обычных видах, тогда как последняя содержит результат рендеринга вида, который рендерится при вызове метода [[yii\base\Controller::render()|render()]] в контроллерах.
Если вы хотите получить доступ к другим данным из шаблона, используйте метод явной передачи (он описан в секции [Доступ к данным в видах](#accessing-data-in-views) настоящего документа). Если вы хотите
передать данные из вида шаблону, вы можете использовать метод, описанный в [передаче данных между видами](#sharing-data-among-views).
Как было описано в секции [Рендеринг в контроллерах](#rendering-in-controllers), когда вы рендерите вид, вызывая метод [[yii\base\Controller::render()|render()]] из контроллера, к результату рендеринга будет применен шаблон. По умолчанию будет использован шаблон `@app/views/layouts/main.php` .
Вы можете использовать разные шаблоны, конфигурируя [[yii\base\Application::layout]] или [[yii\base\Controller::layout]].
Первый переопределяет шаблон, который используется по умолчанию всеми контроллерами, а второй переопределяет шаблон в отдельном контроллере.
Например, код внизу показывает, как можно сделать так, чтобы контроллер использовал шаблон `@app/views/layouts/post.php` при рендеринге вида. Другие контроллеры, если их свойство `layout` не переопределено, все еще будут использовать `@app/views/layouts/main.php` как шаблон.
```php
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public $layout = 'post';
// ...
}
```
Для контроллеров, принадлежащих модулю, вы также можете переопределять свойство модуля [[yii\base\Module::layout|layout]], чтобы
использовать особый шаблон для этих контроллеров.
Поскольку свойство `layout` может быть сконфигурировано на разных уровнях приложения (контроллеры, модули, само приложение),
Yii определяет какой шаблон использовать для контроллера в два этапа.
На первом этапе определяется значение шаблона и контекстный модуль.
- Если [[yii\base\Controller::layout]] свойство контроллера отлично от `null`, используется оно, и [[yii\base\Controller::module|модуль]]
контроллера как контекстный модуль.
- Если [[yii\base\Controller::layout|layout]] равно `null` (не задано), происходит поиск среди родительских модулей контроллера, включая само приложение (которое по умолчанию является родительским модулем для контроллеров, не принадлежащих модулям) и
находится первый модуль, свойство [[yii\base\Module::layout|layout]] которого не равно `null` . Тогда используется найденное значение `layout` этого модуля
и сам модуль в качестве контекста. Если такой модуль не найден, значит шаблон применен не будет.
На втором этапе определяется сам файл шаблона для рендеринга на основании значения `layout` и контекстного модуля.
Значением `layout` может быть:
- Алиас пути (например, `@app/views/layouts/main`).
- Абсолютный путь (например `/main`): значение `layout` начинается со слеша. Будет искаться шаблон из [[yii\base\Application::layoutPath|папки шаблонов]] приложения, по умолчанию это `@app/views/layouts`.
- Относительный путь (например `main`): Будет искаться шаблон из [[yii\base\Module::layoutPath|папки шаблонов контекстного модуля]], по умолчанию это `views/layouts` в [[yii\base\Module::basePath|папке модуля]].
- Булево значение `false`: шаблон не будет применен.
Если у значения `layout` нет расширения, будет использовано расширение по умолчанию - `.php`.
[[yii\base\View|Компоненты вида]] дают много возможностей. Несмотря на то, что существует возможность создавать индивидуальные экземпляры [[yii\base\View]] или дочерних классов, в большинстве случаев используется
сам компонент `view` приложения. Вы можете сконфигурировать компонент в [конфигурации приложения](structure-applications.md#application-configurations) таким образом:
```php
[
// ...
'components' => [
'view' => [
'class' => 'app\components\View',
],
// ...
],
]
```
Компоненты вида предоставляют широкие возможности по работе с видами, они описаны в отдельных секциях документации:
* [темы](output-theming.md): позволяет менять темы оформления для сайта.
* [кэширование фрагментов](caching-fragment.md): позволяет кэшировать фрагменты веб-страниц.
* [работа с клиентскими скриптами](output-client-scripts.md): Поддерживает регистрацию и рендеринг CSS и Javascript.
* [управление связками](structure-assets.md): позволяет регистрацию и управление [связками клиентского кода](structure-assets.md).
* [альтернативные движки шаблонов](tutorial-template-engines.md): позволяет использовать другие шаблонные движки, такие как
Как и [мета тэги](#adding-meta-tags), link тэги полезны во многих случаях, как, например, задание уникальной favicon, указание на RSS фид или указание OpenID сервера для авторизации. С link тэгами можно работать аналогично работе с мета тэгами, вызывая метод [[yii\web\View::registerLinkTag()]]. Например,
вы можете зарегистрировать link тэг в виде таким образом:
```php
$this->registerLinkTag([
'title' => 'Сводка новостей по Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'http://www.yiiframework.com/rss.xml/',
]);
```
Этот код выведет
```html
<linktitle="Сводка новостей по Yii"rel="alternate"type="application/rss+xml"href="http://www.yiiframework.com/rss.xml/">
```
Как и в случае с [[yii\web\View::registerMetaTag()|registerMetaTags()]], вы можете указать ключ вторым параметром при вызове
[[yii\web\View::registerLinkTag()|registerLinkTag()]] чтобы избежать дублирования link тэгов одного типа.
Статическими страницами мы считаем страницы, которые содержат в основном статические данные и для формирования
которых не нужно строить динамические данные в контроллерах.
Вы можете выводить статические страницы, сохраняя их в видах, а затем используя подобный код в контроллере:
```php
public function actionAbout()
{
return $this->render('about');
}
```
Если сайт содержит много статических страниц, описанный выше подход не вполне подходит - его использование
приведет к многократному повторению похожего кода. Вместо этого вы можете использовать [отдельное действие](structure-controllers.md#standalone-actions) [[yii\web\ViewAction]] в контроллере. Например,
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actions()
{
return [
'page' => [
'class' => 'yii\web\ViewAction',
],
];
}
}
```
Теперь, если вы создадите вид `about` в папке `@app/views/site/pages`, он будет отображаться по такому адресу:
```
http://localhost/index.php?r=site/page&view=about
```
`GET` параметр `view` сообщает [[yii\web\ViewAction]] какой вид затребован. Действие будет искать этот вид в папке `@app/views/site/pages`.
Вы можете сконфирурировать параметр [[yii\web\ViewAction::viewPrefix]] чтобы изменить папку в которой ищется вид.