mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +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>
 | ||
| ```
 | ||
| 
 | ||
| > Tip: Несмотря на то, что 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](https://twig.symfony.com/), [Smarty](https://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", однако отрендерен будет только второй.
 | ||
| 
 | ||
| ```php
 | ||
| $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' => 'https://www.yiiframework.com/rss.xml/',
 | ||
| ]);
 | ||
| ```
 | ||
| 
 | ||
| Этот код выведет
 | ||
| 
 | ||
| ```html
 | ||
| <link title="Сводка новостей по Yii" rel="alternate" type="application/rss+xml" href="https://www.yiiframework.com/rss.xml/">
 | ||
| ```
 | ||
| 
 | ||
| Как и в случае с [[yii\web\View::registerMetaTag()|registerMetaTag()]], вы можете указать ключ вторым параметром при вызове 
 | ||
| [[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%2Fpage&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) как строительный материал для видов.
 | ||
| * создавайте и используйте классы-хелперы для изменения и форматирования данных в видах.
 | 
