You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
646 lines
46 KiB
646 lines
46 KiB
10 years ago
|
Виды
|
||
|
====
|
||
|
|
||
|
Виды - это часть [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры, это код, который отвечает за представление данных
|
||
|
конечным пользователям. В веб приложениях виды создаются обычно в виде *видов - шаблонов*, которые суть PHP скрипты, в основном содержащие HTML код
|
||
|
и код PHP, отвечающий за представление и внешний вид. Виды управляются компонентом приложения [[yii\web\View|view]], который содержит часто используемые
|
||
|
методы для упорядочивания видов и их рендеринга. Для упрощения, мы будем называть виды - шаблоны просто видами.
|
||
|
|
||
|
## Создание видов <a name="creating-views"></a>
|
||
|
|
||
|
Как мы упоминали ранее, вид - это просто 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. К тому же, это хороший способ добавления документации в вид.
|
||
|
|
||
|
|
||
|
### Безопасность <a name="security"></a>
|
||
|
|
||
|
При создании видов, которые генерируют 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) отфитрованного результата
|
||
|
|
||
|
|
||
|
### Организация видов <a name="organizing-views"></a>
|
||
|
|
||
|
Как и для [контроллеров](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()]].
|
||
|
|
||
|
|
||
|
## Рендеринг видов <a name="rendering-views"></a>
|
||
|
|
||
|
Вы можете рендерить виды в [контроллерах](structure-controllers.md), [widgets](structure-widgets.md), или из любого другого места, вызывая методы рендеринга видов. Методы вызываются приблизительно так, как это показано в примере ниже,
|
||
|
|
||
|
```
|
||
|
/**
|
||
|
* @param string $view название вида или путь файла, в зависимости от того, какой метод рендеринга используется
|
||
|
* @param array $params данные, которые передаются виду
|
||
|
* @return string результат рендеринга
|
||
|
*/
|
||
|
methodName($view, $params = [])
|
||
|
```
|
||
|
|
||
|
|
||
|
### Рендеринг в контроллерах <a name="rendering-in-controllers"></a>
|
||
|
|
||
|
Внутри [контроллеров](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,
|
||
|
]);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
### Рендеринг в виджетах <a name="rendering-in-widgets"></a>
|
||
|
Внутри [виджетов](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,
|
||
|
]);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
### Рендеринг в видах <a name="rendering-in-views"></a>
|
||
|
|
||
|
Вы можете рендерить вид внутри другого вида используя методы, которые предоставляет [[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') ?>
|
||
|
```
|
||
|
|
||
|
|
||
|
### Рендеринг в других местах <a name="rendering-in-other-places"></a>
|
||
|
|
||
|
Вы может получить доступ к [[yii\base\View|виду]] как компоненту приложения вот так:
|
||
|
`Yii::$app->view`, а затем вызвать вышеупомянутые методы, чтобы отрендерить вид. Например,
|
||
|
|
||
|
```php
|
||
|
// показывает файл "@app/views/site/license.php"
|
||
|
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
|
||
|
```
|
||
|
|
||
|
|
||
|
### Именованные виды <a name="named-views"></a>
|
||
|
|
||
|
При рендеринге вида, вы можете указать нужный вид, используя как имя вида, так и путь к файлу/алиас. В большинстве случаев вы будете использовать первый вариант, т.к. он более нагляден и гибок. Мы называем виды, которые были вызваны с помощью сокращенного имени *именованные виды*.
|
||
|
|
||
|
Имя вида преобразуется в соответствующий ему путь файла в соответствии со следующими правилами:
|
||
|
|
||
|
* Имя вида можно указывать без расширения. В таком случае в качестве расширения будет использоваться `.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()|пути видов]] контекста к имени вида. В основном это применимо к видам, которые рендерятся из контроллеров и виджетов. Например,
|
||
|
`site/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`.
|
||
|
|
||
|
|
||
|
### Доступ к данным из видов <a name="accessing-data-in-views"></a>
|
||
|
|
||
|
Данные можно передавать в вид явно или подгружать их динамически, обращаясь к контексту из вида.
|
||
|
|
||
|
Передавая данные через второй параметр методов рендеринга вида, вы явно передаете данные в вид.
|
||
|
Данные должны быть представлены как обычный массив: ключ-значение. При рендеринге вида, 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 ?>
|
||
|
?>
|
||
|
```
|
||
|
|
||
|
Явная передача данных в вид обычно более предпочтительна, т.к. она делает виды независимыми от контекста. Однако, у нее есть недостаток - необходимость каждый раз вручную строить массив данных, что может быть довольно утомительно и привести к ошибкам, если вид рендерится в разных местах.
|
||
|
|
||
|
|
||
|
### Передача данных между видами <a name="sharing-data-among-views"></a>
|
||
|
|
||
|
[[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'] : [],
|
||
|
]) ?>
|
||
|
```
|
||
|
|
||
|
|
||
|
## Шаблоны <a name="layouts"></a>
|
||
|
|
||
|
Шаблоны - особый тип видов, которые представляют собой общие части разных видов. Например, у большинства страниц веб приложений одинаковые верх и низ (хедер и футер). Можно, конечно, указать их и в каждом виде, однако лучше сделать это один раз, в шаблоне, и затем, при рендеринге, включать уже отрендеренный вид в заданное место шаблона.
|
||
|
|
||
|
### Создание шаблонов <a name="creating-layouts"></a>
|
||
|
|
||
|
Поскольку шаблоны это виды, их можно создавать точно так же, как и обычные виды. По умолчанию шаблоны хранятся в папке `@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>` страницы.
|
||
|
|
||
|
|
||
|
### Доступ к данным в шаблонах <a name="accessing-data-in-layouts"></a>
|
||
|
|
||
|
Внутри шаблона, у вас есть доступ к двум предопределенным переменным: `$this` и `$content`. Первая представляет собой
|
||
|
[[yii\base\View|вид]] компонент, как и в обычных видах, тогда как последняя содержит результат рендеринга вида, который рендерится при вызове метода [[yii\base\Controller::render()|render()]] в контроллерах.
|
||
|
|
||
|
Если вы хотите получить доступ к другим данным из шаблона, используйте метод явной передачи (он описан в секции [Доступ к данным в видах](#accessing-data-in-views) настоящего документа). Если вы хотите
|
||
|
передать данные из вида шаблону, вы можете использовать метод, описанный в [передаче данных между видами](#sharing-data-among-views).
|
||
|
|
||
|
|
||
|
### Использование шаблонов <a name="using-layouts"></a>
|
||
|
|
||
|
Как было описано в секции [Рендеринг в контроллерах](#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`.
|
||
|
|
||
|
### Вложенные шаблоны <a name="nested-layouts"></a>
|
||
|
|
||
|
Иногда нужно вложить один шаблон в другой. Например, в разных разделах сайта используются разные шаблоны, но у всех
|
||
|
этих шаблонов есть основная разметка, которая определяет 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()]] определяет родительский шаблон. Это может быть как
|
||
|
путь к файлу, так и алиас.
|
||
|
|
||
|
Используя подход выше, вы можете вкладывать шаблоны друг в друга в несколько уровней.
|
||
|
|
||
|
|
||
|
### Использование блоков <a name="using-blocks"></a>
|
||
|
|
||
|
Блоки позволяют "записывать" контент в одном месте, а показывать в другом. Они часто используются совместно с шаблонами.
|
||
|
Например, вы определяете (записываете) блок в виде и отображаете его в шаблоне.
|
||
|
|
||
|
Для определения блока вызываются методы [[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; ?>
|
||
|
...
|
||
|
```
|
||
|
|
||
|
|
||
|
## Использование компонентов вида <a name="using-view-components"></a>
|
||
|
|
||
|
[[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/).
|
||
|
|
||
|
|
||
|
Также удобно пользоваться мелкими, но удобными фичами при разработке веб страниц, которые приведены ниже.
|
||
|
|
||
|
|
||
|
### Установка заголовков страниц <a name="setting-page-titles"></a>
|
||
|
|
||
|
У каждой страницы должен быть заголовок. Обычно заголовок выводится в [шаблоне](#layouts). Однако на практике
|
||
|
заголовок часто определяется в видах, а не в шаблонах. Чтобы передать заголовок из вида в шаблон, используется свойство [[yii\web\View::title|title]].
|
||
|
|
||
|
В виде можно задать заголовок таким образом:
|
||
|
|
||
|
```php
|
||
|
<?php
|
||
|
$this->title = 'Мой заголовок страницы';
|
||
|
?>
|
||
|
```
|
||
|
|
||
|
В шаблоне заголовок выводится следующим образом, (убедитесь, что в `<head>` у вас соответствующий код):
|
||
|
|
||
|
```php
|
||
|
<title><?= Html::encode($this->title) ?></title>
|
||
|
```
|
||
|
|
||
|
|
||
|
### Регистрация мета-тэгов <a name="registering-meta-tags"></a>
|
||
|
|
||
|
На веб страницах обычно есть мета-тэги, которые часто используются различными сервисами. Как и заголовки страниц,
|
||
|
мета-тэги выводятся в `<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 <a name="registering-link-tags"></a>
|
||
|
|
||
|
Как и [мета тэги](#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 тэгов одного типа.
|
||
|
|
||
|
|
||
|
## События в видах <a name="view-events"></a>
|
||
|
|
||
|
[[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');
|
||
|
});
|
||
|
```
|
||
|
|
||
|
|
||
|
## Рендеринг статических страниц <a name="rendering-static-pages"></a>
|
||
|
|
||
|
Статическими страницами мы считаем страницы, которые содержат в основном статические данные и для формирования
|
||
|
которых не нужно строить динамические данные в контроллерах.
|
||
|
|
||
|
Вы можете выводить статические страницы, сохраняя их в видах, а затем используя подобный код в контроллере:
|
||
|
|
||
|
```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]] чтобы изменить папку в которой ищется вид.
|
||
|
|
||
|
|
||
|
## Полезные советы <a name="best-practices"></a>
|
||
|
|
||
|
Виды отвечают за представление данных моделей в формате, понятным конечным пользователям. В целом, виды
|
||
|
|
||
|
* должны в основном содержать код, отвечающий за представление, такой как HTML и простой PHP для обхода, форматирования и рендеринга данных.
|
||
|
* не должны содержать кода, который производит запросы к БД. Такими запросами должны заниматься модели.
|
||
|
* должны избегать прямого обращения к данным запроса, таким как `$_GET`, `$_POST`. Разбором запроса должны заниматься контроллеры. Если
|
||
|
данные запросов нужны для построения вида, они должны явно передаваться в вид контроллерами.
|
||
|
* могут читать свойства моделей, но не должны их изменять.
|
||
|
|
||
|
Чтобы сделать виды более управляемыми, избегайте создания видов, которые содержат слишком сложную логику или большое количество кода.
|
||
|
Используйте следующие подходы для их упрощения:
|
||
|
|
||
|
|
||
|
* используйте [шаблоны](#layouts) для отображения основных секций разметки сайта (верхняя часть (хедер), нижняя часть (футер) и т.п.)
|
||
|
* разбивайте сложный вид на несколько видов попроще. Меньшие виды можно рендерить и объединять в больший используя методы рендеринга, описанный в
|
||
|
настоящем документе.
|
||
|
* создавайте и используйте [виджеты](structure-widgets.md) как строительный материал для видов.
|
||
|
* создавайте и используйте классы-хелперы для изменения и форматирования данных в видах.
|