mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			645 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			645 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
Виды
 | 
						||
====
 | 
						||
 | 
						||
Виды - это часть [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры, это код, который отвечает за представление данных 
 | 
						||
конечным пользователям. В веб приложениях виды создаются обычно в виде *видов - шаблонов*, которые суть PHP скрипты, в основном содержащие HTML код 
 | 
						||
и код PHP, отвечающий за представление и внешний вид. Виды управляются компонентом приложения [[yii\web\View|view]], который содержит часто используемые 
 | 
						||
методы для упорядочивания видов и их рендеринга. Для упрощения, мы будем называть виды - шаблоны просто видами.
 | 
						||
 | 
						||
## Создание видов <span id="creating-views"></span>
 | 
						||
 | 
						||
Как мы упоминали ранее, вид - это просто 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>
 | 
						||
 | 
						||
<?php $form = ActiveForm::begin(); ?>
 | 
						||
    <?= $form->field($model, 'username') ?>
 | 
						||
    <?= $form->field($model, 'password')->passwordInput() ?>
 | 
						||
    <?= Html::submitButton('Login') ?>
 | 
						||
<?php ActiveForm::end(); ?>
 | 
						||
```
 | 
						||
 | 
						||
Внутри вида, вы можете использовать `$this`, которое представляет собой [[yii\web\View|компонент вид]], управляющий этим шаблоном и обеспечивающий 
 | 
						||
его рендеринг.
 | 
						||
 | 
						||
Кроме `$this`, в виде могут быть доступны другие переменные, такие как `$form` и `$model` из примера выше. Эти переменные представляют собой данные, которые передаются в вид [контроллерами](structure-controllers.md) или другими объектами, которые вызывают [рендеринг вида](#rendering-views).
 | 
						||
 | 
						||
> Совет: Переданные переменные могут быть перечислены в блоке комментария в начале скрипта, чтобы их смогли распознать IDE. К тому же, это хороший способ добавления документации в вид.  
 | 
						||
 | 
						||
 | 
						||
### Безопасность <span id="security"></span>
 | 
						||
 | 
						||
При создании видов, которые генерируют 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;
 | 
						||
?>
 | 
						||
 | 
						||
<div class="username">
 | 
						||
    <?= Html::encode($user->name) ?>
 | 
						||
</div>
 | 
						||
```
 | 
						||
 | 
						||
Чтобы показать HTML содержимое, используйте [[yii\helpers\HtmlPurifier]] для того, чтобы отфильтровать потенциально опасное содержимое. В примере ниже содержимое поста фильтруется перед показом:
 | 
						||
 | 
						||
```php
 | 
						||
<?php
 | 
						||
use yii\helpers\HtmlPurifier;
 | 
						||
?>
 | 
						||
 | 
						||
<div class="post">
 | 
						||
    <?= HtmlPurifier::process($post->text) ?>
 | 
						||
</div>
 | 
						||
```
 | 
						||
 | 
						||
> Совет: Несмотря на то, что HTMLPurifier отлично справляется с тем, чтобы сделать вывод безопасным, работает он довольно медленно. Если от приложения требуется высокая производительность, рассмотрите возможность [кэширования](caching-overview.md) отфильтрованного результата 
 | 
						||
 | 
						||
 | 
						||
### Организация видов <span id="organizing-views"></span>
 | 
						||
 | 
						||
Как и для [контроллеров](structure-controllers.md), и [моделей](structure-models.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`, где `ПутьВиджета` - это папка, которая содержит класс виджета.
 | 
						||
* С видами, которые рендерятся из других объектов рекомендуется поступать по той же схеме, что и с видами виджетов.
 | 
						||
 | 
						||
В контроллерах и виджетах вы можете изменить папки видов по умолчанию, переопределив метод [[yii\base\ViewContextInterface::getViewPath()]].
 | 
						||
 | 
						||
 | 
						||
## Рендеринг видов <span id="rendering-views"></span>
 | 
						||
 | 
						||
Вы можете рендерить виды в [контроллерах](structure-controllers.md), [widgets](structure-widgets.md), или из любого другого места, вызывая методы рендеринга видов. Методы вызываются приблизительно так, как это показано в примере ниже,
 | 
						||
 | 
						||
```
 | 
						||
/**
 | 
						||
 * @param string $view название вида или путь файла, в зависимости от того, какой метод рендеринга используется 
 | 
						||
 * @param array $params данные, которые передаются виду
 | 
						||
 * @return string результат рендеринга
 | 
						||
 */
 | 
						||
methodName($view, $params = [])
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Рендеринг в контроллерах <span id="rendering-in-controllers"></span>
 | 
						||
 | 
						||
Внутри [контроллеров](structure-controllers.md) можно вызывать следующие методы рендеринга видов:
 | 
						||
 | 
						||
* [[yii\base\Controller::render()|render()]]: рендерит [именованный вид](#named-views) и применяет [шаблон](#layouts)
 | 
						||
  к результату рендеринга.
 | 
						||
* [[yii\base\Controller::renderPartial()|renderPartial()]]: рендерит [именованный вид](#named-views) без шаблона.
 | 
						||
* [[yii\web\Controller::renderAjax()|renderAjax()]]: рендерит [именованный вид](#named-views) без шаблона,
 | 
						||
  и добавляет все зарегистрированные JS/CSS скрипты и стили. Обычно этот метод применяется для рендеринга результата AJAX запроса.
 | 
						||
* [[yii\base\Controller::renderFile()|renderFile()]]: рендерит вид, заданный как путь к файлу или 
 | 
						||
  [алиас](concept-aliases.md).
 | 
						||
 | 
						||
Например,
 | 
						||
 | 
						||
```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;
 | 
						||
        }
 | 
						||
 | 
						||
        // рендерит вид с названием `view` и применяет к нему шаблон
 | 
						||
        return $this->render('view', [
 | 
						||
            'model' => $model,
 | 
						||
        ]);
 | 
						||
    }
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Рендеринг в виджетах <span id="rendering-in-widgets"></span>
 | 
						||
Внутри [виджетов](structure-widgets.md), вы можете вызывать следующие методы для рендеринга видов.
 | 
						||
 | 
						||
* [[yii\base\Widget::render()|render()]]: рендерит [именованный вид](#named-views).
 | 
						||
* [[yii\base\Widget::renderFile()|renderFile()]]: рендерит вид, заданный как путь файла или 
 | 
						||
  [алиас](concept-aliases.md).
 | 
						||
 | 
						||
Например,
 | 
						||
 | 
						||
```php
 | 
						||
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,
 | 
						||
        ]);
 | 
						||
    }
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Рендеринг в видах <span id="rendering-in-views"></span>
 | 
						||
 | 
						||
Вы можете рендерить вид внутри другого вида используя методы, которые предоставляет [[yii\base\View|компонент вида]]:
 | 
						||
 | 
						||
* [[yii\base\View::render()|render()]]: рендерит [именованный вид](#named-views).
 | 
						||
* [[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
 | 
						||
<?= $this->render('_overview') ?>
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Рендеринг в других местах <span id="rendering-in-other-places"></span>
 | 
						||
 | 
						||
Вы может получить доступ к  [[yii\base\View|виду]] как компоненту приложения вот так: 
 | 
						||
`Yii::$app->view`,  а затем вызвать вышеупомянутые методы, чтобы отрендерить вид. Например,
 | 
						||
 | 
						||
```php
 | 
						||
// показывает файл "@app/views/site/license.php"
 | 
						||
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Именованные виды <span id="named-views"></span>
 | 
						||
 | 
						||
При рендеринге вида, вы можете указать нужный вид, используя как имя вида, так и путь к файлу/алиас. В большинстве случаев вы будете использовать первый вариант, т.к. он более нагляден и гибок. Мы называем виды, которые были вызваны с помощью сокращенного имени *именованные виды*.
 | 
						||
 | 
						||
Имя вида преобразуется в соответствующий ему путь файла в соответствии со следующими правилами:
 | 
						||
 | 
						||
* Имя вида можно указывать без расширения. В таком случае в качестве расширения будет использоваться `.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()|пути видов]] контекста к имени вида. В основном это применимо к видам, которые рендерятся из контроллеров и виджетов. Например,
 | 
						||
  `about` будет преобразован в `@app/views/site/about.php` если контекстом является контроллер `SiteController`.
 | 
						||
* Если вид рендерится из другого вида, папка, в которой находится текущий вид будет добавлена к пути вложенного вида. Например, `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`.
 | 
						||
 | 
						||
 | 
						||
### Доступ к данным из видов <span id="accessing-data-in-views"></span>
 | 
						||
 | 
						||
Данные можно передавать в вид явно или подгружать их динамически, обращаясь к контексту из вида.
 | 
						||
 | 
						||
Передавая данные через второй параметр методов рендеринга вида, вы явно передаете данные в вид. 
 | 
						||
Данные должны быть представлены как обычный массив: ключ-значение. При рендеринге вида, 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 ?>
 | 
						||
```
 | 
						||
 | 
						||
Явная передача данных в вид обычно более предпочтительна, т.к. она делает виды независимыми от контекста. Однако, у нее есть недостаток - необходимость каждый раз вручную строить массив данных, что может быть довольно утомительно и привести к ошибкам, если вид рендерится в разных местах.
 | 
						||
 | 
						||
 | 
						||
### Передача данных между видами <span id="sharing-data-among-views"></span>
 | 
						||
 | 
						||
[[yii\base\View|Компонент вида]] имеет свойство [[yii\base\View::params|params]], которое вы можете использовать для обмена данными между видами.
 | 
						||
 | 
						||
Например, в виде `about` вы можете указать текущий сегмент хлебных крошек с помощью следующего кода.
 | 
						||
 | 
						||
```php
 | 
						||
$this->params['breadcrumbs'][] = 'О нас';
 | 
						||
```
 | 
						||
 | 
						||
Затем, в [шаблоне](#layouts), который также является видом, вы можете отобразить хлебные крошки используя данные, переданные через [[yii\base\View::params|params]].
 | 
						||
 | 
						||
```php
 | 
						||
<?= yii\widgets\Breadcrumbs::widget([
 | 
						||
    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
 | 
						||
]) ?>
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
## Шаблоны <span id="layouts"></span>
 | 
						||
 | 
						||
Шаблоны - особый тип видов, которые представляют собой общие части разных видов. Например, у большинства страниц веб приложений одинаковые верх и низ (хедер и футер). Можно, конечно, указать их и в каждом виде, однако лучше сделать это один раз, в шаблоне, и затем, при рендеринге, включать уже отрендеренный вид в заданное место шаблона.
 | 
						||
 | 
						||
### Создание шаблонов <span id="creating-layouts"></span>
 | 
						||
 | 
						||
Поскольку шаблоны это виды, их можно создавать точно так же, как и обычные виды. По умолчанию шаблоны хранятся в папке `@app/views/layouts`. Шаблоны, которые используются в конкретном [модуле](structure-modules.md), хранятся в подпапке `views/layouts` [[yii\base\Module::basePath|папки модуля]]. Вы можете изменить папку шаблонов по умолчанию, используя свойство [[yii\base\Module::layoutPath]] приложения или модулей.
 | 
						||
 | 
						||
Пример ниже показывает как выглядит шаблон. Для лучшего понимания мы сильно упростили код шаблона. На практике, однако, в нем часто содержится больше кода, например, тэги `<head>`, главное меню и т.д. 
 | 
						||
 | 
						||
```php
 | 
						||
<?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>Моя компания</header>
 | 
						||
    <?= $content ?>
 | 
						||
    <footer>Моя компания © 2014</footer>
 | 
						||
<?php $this->endBody() ?>
 | 
						||
</body>
 | 
						||
</html>
 | 
						||
<?php $this->endPage() ?>
 | 
						||
```
 | 
						||
 | 
						||
Как видите, шаблон генерирует HTML тэги, которые присутствуют на всех страницах. Внутри секции `<body>`, шаблон выводит переменную `$content`, которая  содержит  результат рендеринга видов контента, который передается в шаблон, при работе метода  [[yii\base\Controller::render()]].
 | 
						||
 | 
						||
Большинство шаблонов вызывают методы, аналогично тому, как это сделано в примере выше, чтобы скрипты и тэги, зарегистированные в других местах приложения могли быть правильно отображены в местах вызова (например, в шаблоне).
 | 
						||
 | 
						||
- [[yii\base\View::beginPage()|beginPage()]]: Этот метод нужно вызывать в самом начале шаблона.
 | 
						||
  Он вызывает событие [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]], которое происходит при начале обработки страницы.
 | 
						||
- [[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>` страницы.
 | 
						||
 | 
						||
 | 
						||
### Доступ к данным в шаблонах <span id="accessing-data-in-layouts"></span>
 | 
						||
 | 
						||
Внутри шаблона, у вас есть доступ к двум предопределенным переменным: `$this` и `$content`. Первая представляет собой 
 | 
						||
 [[yii\base\View|вид]] компонент, как и в обычных видах, тогда как последняя содержит результат рендеринга вида, который рендерится при вызове метода [[yii\base\Controller::render()|render()]] в контроллерах.
 | 
						||
 | 
						||
Если вы хотите получить доступ к другим данным из шаблона, используйте метод явной передачи (он описан в секции [Доступ к данным в видах](#accessing-data-in-views) настоящего документа). Если вы хотите 
 | 
						||
передать данные из вида шаблону, вы можете использовать метод, описанный в [передаче данных между видами](#sharing-data-among-views).
 | 
						||
 | 
						||
 | 
						||
### Использование шаблонов <span id="using-layouts"></span>
 | 
						||
 | 
						||
Как было описано в секции [Рендеринг в контроллерах](#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`.
 | 
						||
 | 
						||
### Вложенные шаблоны <span id="nested-layouts"></span>
 | 
						||
 | 
						||
Иногда нужно вложить один шаблон в другой. Например, в разных разделах сайта используются разные шаблоны, но у всех 
 | 
						||
этих шаблонов есть основная разметка, которая определяет HTML5 структуру страницы. Вы можете использовать вложенные шаблоны, 
 | 
						||
вызывая [[yii\base\View::beginContent()|beginContent()]] и [[yii\base\View::endContent()|endContent()]] в дочерних 
 | 
						||
шаблонах таким образом:
 | 
						||
 | 
						||
```php
 | 
						||
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
 | 
						||
 | 
						||
...код дочернего шаблона...
 | 
						||
 | 
						||
<?php $this->endContent(); ?>
 | 
						||
```
 | 
						||
 | 
						||
В коде выше дочерний шаблон заключается в [[yii\base\View::beginContent()|beginContent()]] и [[yii\base\View::endContent()|endContent()]].
 | 
						||
Параметр, передаваемый в метод [[yii\base\View::beginContent()|beginContent()]] определяет родительский шаблон. Это может быть как 
 | 
						||
путь к файлу, так и алиас.
 | 
						||
 | 
						||
Используя подход выше, вы можете вкладывать шаблоны друг в друга в несколько уровней.
 | 
						||
 | 
						||
 | 
						||
### Использование блоков <span id="using-blocks"></span>
 | 
						||
 | 
						||
Блоки позволяют "записывать" контент в одном месте, а показывать в другом. Они часто используются совместно с шаблонами. 
 | 
						||
Например, вы определяете (записываете) блок в виде и отображаете его в шаблоне.
 | 
						||
 | 
						||
Для определения блока вызываются методы [[yii\base\View::beginBlock()|beginBlock()]] и [[yii\base\View::endBlock()|endBlock()]]. 
 | 
						||
После определения, блок доступен через `$view->blocks[$blockID]`, где `$blockID` - это уникальный ID, который вы присваиваете блоку
 | 
						||
в начале определения. 
 | 
						||
 | 
						||
В примере ниже показано, как можно использовать блоки, определенные в виде, чтобы динамически изменять фрагменты шаблона.
 | 
						||
 | 
						||
Сначала, в виде, вы записываете один или несколько блоков:
 | 
						||
 | 
						||
```php
 | 
						||
...
 | 
						||
 | 
						||
<?php $this->beginBlock('block1'); ?>
 | 
						||
 | 
						||
...содержимое блока 1...
 | 
						||
 | 
						||
<?php $this->endBlock(); ?>
 | 
						||
 | 
						||
...
 | 
						||
 | 
						||
<?php $this->beginBlock('block3'); ?>
 | 
						||
 | 
						||
...содержимое блока 3...
 | 
						||
 | 
						||
<?php $this->endBlock(); ?>
 | 
						||
```
 | 
						||
 | 
						||
Затем, в шаблоне, рендерите блоки если они есть, или показываете контент по умолчанию, если блок не определен.
 | 
						||
 | 
						||
```php
 | 
						||
...
 | 
						||
<?php if (isset($this->blocks['block1'])): ?>
 | 
						||
    <?= $this->blocks['block1'] ?>
 | 
						||
<?php else: ?>
 | 
						||
    ... контент по умолчанию для блока 1 ...
 | 
						||
<?php endif; ?>
 | 
						||
 | 
						||
...
 | 
						||
 | 
						||
<?php if (isset($this->blocks['block2'])): ?>
 | 
						||
    <?= $this->blocks['block2'] ?>
 | 
						||
<?php else: ?>
 | 
						||
    ... контент по умолчанию для блока 2 ...
 | 
						||
<?php endif; ?>
 | 
						||
 | 
						||
...
 | 
						||
 | 
						||
<?php if (isset($this->blocks['block3'])): ?>
 | 
						||
    <?= $this->blocks['block3'] ?>
 | 
						||
<?php else: ?>
 | 
						||
    ... контент по умолчанию для блока 3 ...
 | 
						||
<?php endif; ?>
 | 
						||
...
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
## Использование компонентов вида <span id="using-view-components"></span>
 | 
						||
 | 
						||
[[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): позволяет использовать другие шаблонные движки, такие как 
 | 
						||
  [Twig](http://twig.sensiolabs.org/), [Smarty](http://www.smarty.net/).
 | 
						||
 | 
						||
 | 
						||
Также удобно пользоваться мелкими, но удобными фичами при разработке веб страниц, которые приведены ниже.
 | 
						||
 | 
						||
 | 
						||
### Установка заголовков страниц <span id="setting-page-titles"></span>
 | 
						||
 | 
						||
У каждой страницы должен быть заголовок. Обычно заголовок выводится в [шаблоне](#layouts). Однако на практике 
 | 
						||
заголовок часто определяется в видах, а не в шаблонах. Чтобы передать заголовок из вида в шаблон, используется свойство [[yii\web\View::title|title]]. 
 | 
						||
 | 
						||
В виде можно задать заголовок таким образом:
 | 
						||
 | 
						||
```php
 | 
						||
<?php
 | 
						||
$this->title = 'Мой заголовок страницы';
 | 
						||
?>
 | 
						||
```
 | 
						||
 | 
						||
В шаблоне заголовок выводится следующим образом, (убедитесь, что в `<head>` у вас соответствующий код):
 | 
						||
 | 
						||
```php
 | 
						||
<title><?= Html::encode($this->title) ?></title>
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Регистрация мета-тэгов <span id="registering-meta-tags"></span>
 | 
						||
 | 
						||
На веб страницах обычно есть мета-тэги, которые часто используются различными сервисами. Как и заголовки страниц, 
 | 
						||
мета-тэги выводятся в `<head>` и обычно генерируются в шаблонах.
 | 
						||
 | 
						||
Если вы хотите указать, какие мета-тэги генерировать в видах, вы можете вызвать метод [[yii\web\View::registerMetaTag()]] в виде так, 
 | 
						||
как в примере ниже: 
 | 
						||
 | 
						||
```php
 | 
						||
<?php
 | 
						||
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
 | 
						||
?>
 | 
						||
```
 | 
						||
 | 
						||
Этот код зарегистрирует мета тэг "keywords" в виде. Зарегистрированные мета тэги рендерятся после того, как закончен 
 | 
						||
рендеринг шаблона. Они вставляются в то место, где в шаблоне вызван метод [[yii\web\View::head()]]. Результатом рендеринга 
 | 
						||
мета тэгов является следующий код:
 | 
						||
 | 
						||
```php
 | 
						||
<meta name="keywords" content="yii, framework, php">
 | 
						||
```
 | 
						||
 | 
						||
Обратите внимание, что при вызове метода [[yii\web\View::registerMetaTag()]] несколько раз мета тэги будут регистироваться 
 | 
						||
каждый раз без проверки на уникальность.
 | 
						||
 | 
						||
Чтобы убедиться, что зарегистрирован только один экземпляр одного типа мета тэгов, вы можете указать ключ мета тэга в качестве второго 
 | 
						||
параметра при вызове метода.
 | 
						||
К примеру, следующий код регистрирует два мета тэга "description", однако отрендерен будет только второй.
 | 
						||
 | 
						||
```html
 | 
						||
$this->registerMetaTag(['name' => 'description', 'content' => 'Мой сайт сделан с помощью Yii!'], 'description');
 | 
						||
$this->registerMetaTag(['name' => 'description', 'content' => 'Это сайт о забавных енотах.'], 'description');
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Регистрация тэгов link <span id="registering-link-tags"></span>
 | 
						||
 | 
						||
Как и [мета тэги](#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
 | 
						||
<link title="Сводка новостей по 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 тэгов одного типа.
 | 
						||
 | 
						||
 | 
						||
## События в видах <span id="view-events"></span>
 | 
						||
 | 
						||
[[yii\base\View|Компонент вида]] вызывает несколько событий во время рендеринга. 
 | 
						||
Вы можете задавать обработчики для этих событий чтобы добавлять контент 
 | 
						||
в вид или делать пост-обработку результатов рендеринга до того, как они будут отправлены конечным пользователям.
 | 
						||
 | 
						||
- [[yii\base\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: вызывается в начале рендеринга файла в контроллере. 
 | 
						||
Обработчики этого события могут придать атрибуту [[yii\base\ViewEvent::isValid]] значение `false`, чтобы отменить процесс рендеринга.
 | 
						||
- [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: событие инициируется после рендеринга файла вызовом [[yii\base\View::afterRender()]].
 | 
						||
  Обработчики события могут получать результат рендеринга через [[yii\base\ViewEvent::output]] и могут изменять это свойство для изменения 
 | 
						||
результата рендеринга.
 | 
						||
- [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: инициируется вызовом [[yii\base\View::beginPage()]] в шаблонах.
 | 
						||
- [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]]: инициируется вызовом [[yii\base\View::endPage()]] в шаблонах.
 | 
						||
- [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: инициируется вызовом [[yii\web\View::beginBody()]] в шаблонах.
 | 
						||
- [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]]: инициируется вызовом [[yii\web\View::endBody()]] в шаблонах.
 | 
						||
 | 
						||
Например, следующий код вставляет дату в конец `body` страницы:
 | 
						||
 | 
						||
```php
 | 
						||
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
 | 
						||
    echo date('Y-m-d');
 | 
						||
});
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
## Рендеринг статических страниц <span id="rendering-static-pages"></span>
 | 
						||
 | 
						||
Статическими страницами мы считаем страницы, которые содержат в основном статические данные и для формирования 
 | 
						||
которых не нужно строить динамические данные в контроллерах.
 | 
						||
 | 
						||
Вы можете выводить статические страницы, сохраняя их в видах, а затем используя подобный код в контроллере:
 | 
						||
 | 
						||
```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]] чтобы изменить папку в которой ищется вид.
 | 
						||
 | 
						||
 | 
						||
## Полезные советы <span id="best-practices"></span>
 | 
						||
 | 
						||
Виды отвечают за представление данных моделей в формате, понятным конечным пользователям. В целом, виды
 | 
						||
 | 
						||
* должны в основном содержать код, отвечающий за представление, такой как HTML и простой PHP для обхода, форматирования и рендеринга данных.
 | 
						||
* не должны содержать кода, который производит запросы к БД. Такими запросами должны заниматься модели.
 | 
						||
* должны избегать прямого обращения к данным запроса, таким как `$_GET`, `$_POST`. Разбором запроса должны заниматься контроллеры. Если 
 | 
						||
данные запросов нужны для построения вида, они должны явно передаваться в вид контроллерами.
 | 
						||
* могут читать свойства моделей, но не должны их изменять.
 | 
						||
 | 
						||
Чтобы сделать виды более управляемыми, избегайте создания видов, которые содержат слишком сложную логику или большое количество кода. 
 | 
						||
Используйте следующие подходы для их упрощения:
 | 
						||
 | 
						||
 | 
						||
* используйте [шаблоны](#layouts) для отображения основных секций разметки сайта (верхняя часть (хедер), нижняя часть (футер) и т.п.)
 | 
						||
* разбивайте сложный вид на несколько видов попроще. Меньшие виды можно рендерить и объединять в больший используя методы рендеринга, описанный в 
 | 
						||
настоящем документе.
 | 
						||
* создавайте и используйте [виджеты](structure-widgets.md) как строительный материал для видов.
 | 
						||
* создавайте и используйте классы-хелперы для изменения и форматирования данных в видах.
 |