Представления являются частью архитектуры MVC. Они являются кодом, ответственным за представление данных конечным пользователям. В веб-приложении представления обычно создаются с точки зрения шаблонов представлений, которые представляют собой файлы PHP-скриптов, содержащие главным образом код HTML и представляющий PHP-код. Они управляются компонентом приложения-представления, который предоставляет общеупотребительные методы для облегчения компоновки и рендеринга представления. Для простоты мы часто вызываем шаблоны представлений или просматриваем файлы шаблонов в виде представлений.

Создание представления

Как уже упоминалось, представление представляет собой просто PHP-скрипт, смешанный с HTML и PHP-кодом. Ниже приведено представление формы входа в систему. Как вы можете видеть, PHP-код используется для генерации динамического содержимого, такого как заголовок страницы и форма, в то время как HTML-код организует их в презентабельную HTML-страницу.

<?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 = 'Login';
?>
<h1><?= Html::encode($this->title) ?></h1>

<p>Please fill out the following fields to login:</p>

<?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'username') ?>
    <?= $form->field($model, 'password')->passwordInput() ?>
    <?= Html::submitButton('Login') ?>
<?php ActiveForm::end(); ?>

В представлении вы можете получить доступ к $this, которая ссылается на компонент представления, управляющий и отображающий этот шаблон представления.

Помимо $this, в представлении могут быть другие предопределенные переменные, такие как $model в приведенном выше примере. Эти переменные представляют данные, которые помещаются в представление контроллерами или другими объектами, которые запускают визуализацию представления.

Безопасность

При создании представлений, генерирующих HTML-страницы, важно, чтобы вы кодировали или фильтровали данные, поступающие от конечных пользователей, перед их представлением. В противном случае ваше приложение может подвергаться атакам межсайтового скриптинга.

Чтобы отобразить простой текст, сначала закодируйте его, вызывая yii\helpers\Html::encode(). Например, следующий код кодирует имя пользователя перед его отображением:

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

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

Чтобы отобразить содержимое HTML, используйте yii\helpers\HtmlPurifier для фильтрации содержимого. Например, следующий код фильтрует содержимое сообщения перед его отображением:

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

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

Организация представления

Как и контроллеры и модели, существуют соглашения для организации представления.

  • Для представлений, создаваемых контроллером, они должны быть по умолчанию помещены в каталог @app/views/ControllerID, где ControllerID ссылается на ID контроллера. Например, если класс контроллера является PostController, в качестве каталога будет использоваться @app/views/post. Если это PostCommentController, в качестве каталога будет использоваться @app/views/post-comment. В случае, если контроллер принадлежит к модулю, каталог будет представлять собой views/ControllerID в каталоге модуля.
  • Для представлений, отображаемых в виджете, они должны быть по умолчанию помещены в каталог WidgetPath/views, где WidgetPath обозначает каталог, содержащий файл класса виджета.
  • Для представлений, создаваемых другими объектами, рекомендуется придерживаться такого же соглашения, что и для виджетов.

Вы можете настроить эти каталоги представлений по умолчанию, переопределив метод контроллеров или виджетов yii\base\ViewContextInterface::getViewPath().

Рендеринг просмотра

Вы можете визуализировать представления в контроллерах, виджетах или любых других местах, вызывая методы визуализации представления.

/**
 * @param string $view просмотреть имя или путь к файлу, в зависимости от метода фактического рендеринга
 * @param array $params данные, передаваемые в представление
 * @return string отображение результата
 */
methodName($view, $params = [])

Рендеринг в контроллерах

Внутри контроллеров вы можете вызвать следующие методы контроллера для визуализации представлений:

  • render(): визуализирует именованный вид и применяет макет к результату рендеринга.
  • renderPartial(): отображает именованное представление без макета.
  • renderAjax(): отображает именованный вид без макета и внедряет все зарегистрированные сценарии и файлы JS/CSS. Обычно используется в ответ на веб-запросы AJAX.
  • renderFile(): отображает представление, указанное в терминах пути или псевдонима файла представления.
  • renderContent(): визуализирует статическую строку, вставляя ее в текущую применимую компоновку.
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,
        ]);
    }
}

Визуализация в виджетах

В виджетах вы можете вызывать следующие методы виджетов для визуализации представлений.

  • render(): отображает именованное представление.
  • renderFile(): отображает представление, указанное в терминах пути или псевдонима файла представления.
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,
        ]);
    }
}

Рендеринг в представлениях

Вы можете отобразить представление в другом представлении, вызвав один из следующих методов, предоставляемых компонентом представления:

  • render(): отображает именованное представление.
  • renderAjax(): отображает именованный вид и внедряет все зарегистрированные JS/CSS-скрипты и файлы. Обычно используется в ответ на веб-запросы AJAX.
  • renderFile(): отображает представление, указанное в терминах пути или псевдонима файла представления.

Например, следующий код в представлении отображает файл представления _overview.php, который находится в том же каталоге, что и представление, отображаемое в настоящее время. Помните, что $this в представлении ссылается на компонент представления:

<?= $this->render('_overview') ?>

Рендеринг в других местах

В любом месте вы можете получить доступ к компоненту приложения вида с помощью выражения Yii::$ app->view, а затем вызвать его вышеупомянутые методы для визуализации представления. Например:

// отображение файла "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');

Именованные представления

Когда вы визуализируете представление, вы можете указать вид, используя либо имя представления, либо путь/псевдоним файла представления. В большинстве случаев вы бы использовали первый, потому что он более краткий и гибкий. Мы вызываем представления, указанные с использованием имен в виде именованных представлений.

Имя представления преобразуется в соответствующий путь файла представления в соответствии со следующими правилами:

  • Имя представления может опускать имя расширения файла. В этом случае .php будет использоваться в качестве расширения. Например, имя представления about соответствует файловому имени about.php.
  • Если имя представления начинается с двойных слэшей //, соответствующий путь к файлу представления будет иметь вид @app/view/ViewName. То есть представление просматривается в соответствии с путем просмотра приложения. Например, //site/about будет разрешен в @app/views/site/about.php.
  • Если имя представления начинается с одной косой черты /, путь к файлу представления формируется путем префикса имени представления с путем просмотра текущего активного модуля. Если активного модуля нет, будет использоваться @app/views/ViewName. Например, /user/create будет разрешен в @app/modules/user/views/user/create.php, если текущий активный модуль является user. Если активного модуля нет, путь к файлу представления будет иметь вид @app/views/user/create.php.
  • Если представление отображается с контекстом, а контекст реализует yii\base\ViewContextInterface, путь к файлу представления формируется путем префикса пути представления контекста к имени представления. В основном это относится к представлениям, которые отображаются в контроллерах и виджетах. Например, about будет разрешен в @app/views/site/about.php, если контекст является контроллером SiteController.
  • Если представление отображается в другом представлении, каталог, содержащий другой файл представления, будет иметь префикс к новому имени представления, чтобы сформировать фактический путь к файлу представления. Например, элемент будет разрешен в @app/views/post/item.php, если он отображается в представлении @app/views/post/index.php.

Согласно вышеприведенным правилам вызов $this->render('view') в контроллере app\controllers\PostController фактически визуализирует файл вида @app/views/post/view.php, вызывая $this->render('_overview') в этом представлении отобразит файл вида @app/views/post/_overview.php.

Доступ к данным в представлениях

Существует два подхода к данным в представлении: push и pull.

Передавая данные в качестве второго параметра методам визуализации представления, вы используете push-подход. Данные должны быть представлены в виде массива пар имя-значение. Когда представление визуализируется, функция PHP extract() будет вызываться в этом массиве, чтобы массив был извлечен в переменные в представлении. Например, следующий код визуализации представления в контроллере будет сдвигать две переменные в представление отчета: $foo = 1 и $bar = 2.

echo $this->render('report', [
    'foo' => 1,
    'bar' => 2,
]);

Метод pull активно извлекает данные из компонента представления или других объектов, доступных в представлениях (например, Yii::$app). Используя приведенный ниже код в качестве примера, в представлении вы можете получить объект контроллера с помощью выражения $this->context. И, как результат, вы можете получить доступ к любым свойствам или методам контроллера в представлении report, например, идентификатор контроллера, показанный ниже:

<?= $this->context->id ?>

Подталкивающий подход обычно является предпочтительным способом доступа к данным в представлениях, поскольку он делает представления менее зависимыми от контекстных объектов. Его недостатком является то, что вам нужно вручную создавать массив данных все время, что может стать утомительным и подверженным ошибкам, если представление совместно используется и визуализируется в разных местах.

Совместное использование данных между представлениями

Компонент представления предоставляет свойство params, которое можно использовать для совместного использования данных между представлениями. Например, в представлении "about" вы можете иметь следующий код, который определяет текущий сегмент хлебных крошек.

$this->params['breadcrumbs'][] = 'О нас';

Затем в файле layout, который также является представлением, вы можете отображать хлебные крошки, используя данные, переданные по параметрам:

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

Layouts

Layouts - это особый тип представлений, которые представляют собой общие части нескольких представлений. Например, страницы для большинства веб-приложений используют один и тот же заголовок и нижний колонтитул страницы. Хотя вы можете повторять один и тот же заголовок и нижний колонтитул страницы в каждом представлении, лучше сделать это один раз в макете и вставить результат рендеринга представления содержимого в соответствующее место в макете.

Создание макетов

Поскольку макеты также являются представлениями, их можно создать аналогично нормальным представлениям. По умолчанию макеты хранятся в каталоге @app/views/layouts. Для макетов, используемых в модуле, они должны храниться в каталоге views/layouts в каталоге модуля. Вы можете настроить каталог макетов по умолчанию, настроив свойство yii\base\Module::$layoutPath приложения или модулей.

Следующий пример показывает, как выглядит макет. Обратите внимание, что в иллюстративных целях мы значительно упростили код в макете. На практике вы можете добавить в него больше контента, например теги заголовка, главное меню и т. д.

<?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>&copy; 2018 by My Blog</footer>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>

Как вы можете видеть, макет генерирует теги HTML, общие для всех страниц. В разделе <body> макет перекликается с переменной $content, которая представляет результат рендеринга представлений контента, и помещается в макет при вызове yii\base\Controller::render().

Большинство макетов должны вызывать следующие методы, как показано в приведенном выше коде. Эти методы главным образом вызывают события, связанные с процессом рендеринга, чтобы сценарии и теги, зарегистрированные в других местах, могли быть правильно введены в места, где вызываются эти методы.

  • beginPage(): этот метод должен вызываться в самом начале макета. Он вызывает событие EVENT_BEGIN_PAGE, которое указывает начало страницы.
  • endPage(): этот метод должен вызываться в конце макета. Он вызывает событие EVENT_END_PAGE, которое указывает конец страницы.
  • head(): этот метод должен вызываться в разделе <head> HTML-страницы. Он создает местозаполнитель, который будет заменен зарегистрированным HTML-кодом руководителя (например, тегами ссылок, метатегами), когда страница заканчивает рендеринг.
  • beginBody(): этот метод должен вызываться в начале раздела <body>. Он запускает событие EVENT_BEGIN_BODY и создает местозаполнитель, который будет заменен зарегистрированным кодом HTML (например, JavaScript), нацеленным на начальную позицию тела.
  • endBody(): этот метод должен вызываться в конце раздела <body>. Он запускает событие EVENT_END_BODY и создает местозаполнитель, который будет заменен зарегистрированным кодом HTML (например, JavaScript), нацеленным на конечную позицию тела.

Доступ к данным в макетах

В макете у вас есть доступ к двум предопределенным переменным: $this и $content. Первый относится к компоненту представления, как в обычных представлениях, в то время как последний содержит результат визуализации представления содержимого, которое визуализируется вызовом метода render() в контроллерах.

Использование макетов

Когда вы визуализируете представление, вызывая метод render() в контроллере, макет будет применен к результату рендеринга. По умолчанию будет использоваться layout @app/views/layouts/main.php.

Вы можете использовать другой макет, настроив конфигурацию yii\base\Application::$layout или yii\base\Controller::$layout. Первый определяет макет, используемый всеми контроллерами, в то время как последний переопределяет первый для отдельных контроллеров. Например, следующий код заставляет пост-контроллер использовать @app/views/layouts/post.php в качестве макета при визуализации своих представлений. Другие контроллеры, при условии, что их свойство макета не затронуто, будут по-прежнему использовать в качестве макета стандартное приложение @app/views/layouts/main.php.

namespace app\controllers;

use yii\web\Controller;

class PostController extends Controller
{
    public $layout = 'post';
    
    // ...
}

Для контроллеров, принадлежащих модулю, вы также можете настроить свойство макета модуля, чтобы использовать определенный макет для этих контроллеров.

Поскольку свойство макета может быть настроено на разных уровнях (контроллеры, модули, приложение), за сценой Yii выполняет два шага, чтобы определить, какой файл фактического макета используется для конкретного контроллера. На первом этапе он определяет значение макета и контекстный модуль:

  • Если свойство yii\base\Controller::$layout контроллера не равно NULL, используйте его в качестве значения макета и модуля контроллера в качестве контекстного модуля.
  • Если свойство yii\base\Controller::$layout контроллера равно null, выполните поиск по всем модулям предка (включая само приложение) контроллера и найдите первый модуль, у которого свойство макета не равно NULL. Используйте этот модуль и его значение макета как контекстный модуль и выбранное значение макета. Если такой модуль не может быть найден, это означает, что макет не будет применен.

На втором этапе он определяет фактический файл макета в соответствии со значением макета и контекстным модулем, определенным на первом этапе. Значение макета может быть:

  • Псевдоним пути (например, @app/views/layouts/main).
  • Абсолютный путь (например, /main): значение макета начинается с косой черты. Фактический файл макета будет искать по пути компоновки приложения, который по умолчанию имеет вид @app/views/layouts.
  • Относительный путь (например, main): фактический файл макета будет искать по пути компоновки контекстного модуля, который по умолчанию находится в каталоге views/layouts в каталоге модуля.
  • Логическое значение false: макет не будет применен.

Если значение макета не содержит расширения файла, он будет использовать файл с расширением .php по умолчанию.

Вложенные макеты

Иногда вы можете захотеть вложить один макет в другой. Например, в разных разделах веб-сайта вы хотите использовать разные макеты, тогда как все эти макеты используют один и тот же базовый макет, который генерирует общую структуру страницы HTML5. Вы можете достичь этой цели, вызывая функции beginContent() и endContent() в дочерних макетах, например:

<?php $this->beginContent('@app/views/layouts/base.php'); ?>

...здесь дочерний макет...

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

Как показано выше, дочерний макет должен быть заключен в пределах beginContent() и endContent(). Параметр, переданный в beginContent(), указывает, что является макетом родителя. Это может быть файл макета или псевдоним. Используя вышеописанный подход, вы можете встраивать макеты более чем в один уровень.

Использование блоков

Блоки позволяют вам указывать содержимое представления в одном месте, показывая его в другом. Они часто используются вместе с макетами. Например, вы можете определить блок в представлении содержимого и отобразить его в макете.

Вы вызываете beginBlock() и endBlock(), чтобы определить блок. Затем к кадру можно получить доступ через $view->blocks[$blockID], где $blockID означает уникальный идентификатор, который вы назначаете блоку при его определении.

В следующем примере показано, как можно использовать блоки для настройки определенных частей макета в представлении содержимого.

Во-первых, в представлении содержимого определите один или несколько блоков:

...

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

...content of block1...

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

...

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

...content of block3...

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

Затем в представлении макета рендеринг блоков, если они доступны, или отображение некоторого содержимого по умолчанию, если блок не определен.

...
<?php if (isset($this->blocks['block1'])): ?>
    <?= $this->blocks['block1'] ?>
<?php else: ?>
    ... default content for block1 ...
<?php endif; ?>

...

<?php if (isset($this->blocks['block2'])): ?>
    <?= $this->blocks['block2'] ?>
<?php else: ?>
    ... default content for block2 ...
<?php endif; ?>

...

<?php if (isset($this->blocks['block3'])): ?>
    <?= $this->blocks['block3'] ?>
<?php else: ?>
    ... default content for block3 ...
<?php endif; ?>
...

Использование компонентов представления

Просмотр компонентов предоставляет множество функций, связанных с представлением. Хотя вы можете получать компоненты вида, создавая отдельные экземпляры yii\base\View или его дочернего класса, в большинстве случаев вы будете главным образом использовать компонент приложения вида. Вы можете настроить этот компонент в конфигурациях приложений, например:

[
    // ...
    'components' => [
        'view' => [
            'class' => 'app\components\View',
        ],
        // ...
    ],
]

Компоненты представления предоставляют следующие полезные функции, связанные с видом, каждый из которых описан более подробно в отдельном разделе:

  • theming: позволяет разрабатывать и изменять тему для вашего веб-сайта.
  • fragment caching: позволяет кэшировать фрагмент внутри веб-страницы.
  • client script handling: поддержка регистрации и рендеринга CSS и JavaScript.
  • asset bundle handling: поддерживает регистрацию и предоставление пакетов активов.
  • alternative template engines: позволяет вам использовать другие движки шаблонов, такие как Twig, Smarty.

Вы также можете часто использовать следующие вспомогательные, но полезные функции при разработке веб-страниц.

Настройка заголовков страниц

Каждая веб-страница должна иметь заголовок. Обычно тег заголовка отображается в макете. Однако на практике название часто определяется в виде содержимого, а не в макете. Чтобы решить эту проблему, yii\web\View предоставляет свойство title для передачи информации о заголовке из представлений содержимого в макеты.

Чтобы использовать эту функцию, в каждом представлении содержимого вы можете установить заголовок страницы следующим образом:

<?php
$this->title = 'Тайтл страницы';
?>

Затем в макете убедитесь, что у вас есть следующий код в разделе <head>:

<title><?= Html::encode($this->title) ?></title>

Регистрация метатегов

Веб-страницы обычно должны генерировать различные мета-теги, необходимые для разных сторон. Как и заголовки страниц, метатеги появляются в разделе <head> и обычно генерируются в макетах.

Если вы хотите указать, какие метатеги будут генерироваться в представлениях контента, вы можете вызвать yii\web\View::registerMetaTag() в представлении содержимого, например:

<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>

В приведенном выше коде будет зарегистрирован метатег «ключевые слова» с компонентом представления. Зарегистрированный метатег рендерится после того, как макет завершит рендеринг. Следующий код HTML будет сгенерирован и вставлен в том месте, где вы вызываете yii\web\View::head() в макете:

<meta name="keywords" content="yii, framework, php">

Обратите внимание, что если вы несколько раз назовете yii\web\View::registerMetaTag(), он зарегистрирует несколько метатег, независимо от того, совпадают ли метатеги или нет.

Чтобы убедиться, что есть только один экземпляр типа метатега, вы можете указать ключ в качестве второго параметра при вызове метода. Например, следующий код регистрирует два «description» метатега. Тем не менее, будет отображаться только второй.

$this->registerMetaTag(['name' => 'description', 'content' => 'Это мой классный сайт, сделанный с Yii!'], 'description');
$this->registerMetaTag(['name' => 'description', 'content' => 'Этот веб-сайт посвящен забавным енотам.'], 'description');

Регистрация тегов ссылок

Как и метатеги, теги ссылок полезны во многих случаях, например, настройка значка, указание на RSS-канал или делегирование OpenID другому серверу. Вы можете работать с тегами ссылок аналогично метатегам с помощью yii\web\View::registerLinkTag(). Например, в представлении содержимого вы можете зарегистрировать тег ссылки, как показано ниже:

$this->registerLinkTag([
    'title' => 'Live News for Yii',
    'rel' => 'alternate',
    'type' => 'application/rss+xml',
    'href' => 'http://www.yiiframework.com/rss.xml',
]);
<link title="Новости для Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml">

Подобно registerMetaTag(), вы можете указать ключ при вызове registerLinkTag(), чтобы избежать генерации повторяющихся тегов ссылок.

Просмотр событий

Просмотр компонентов запускает несколько событий в процессе визуализации представления. Вы можете отвечать на эти события, чтобы вставлять содержимое в представления или обрабатывать результаты рендеринга до их отправки конечным пользователям.

  • EVENT_BEFORE_RENDER: запускается в начале рендеринга файла в контроллере. Обработчики этого события могут установить значение yii\base\ViewEvent::$isValid равным false, чтобы отменить процесс рендеринга.
  • EVENT_AFTER_RENDER: запускается после рендеринга файла вызовом yii\base\View::afterRender(). Обработчики этого события могут получить результат визуализации через вывод yii\base\ViewEvent::$ и могут изменить это свойство, чтобы изменить результат рендеринга.
  • EVENT_BEGIN_PAGE: вызвано вызовом yii\base\View::beginPage() в макетах.
  • EVENT_END_PAGE: вызвано вызовом yii\base\View::endPage() в макетах.
  • EVENT_BEGIN_BODY: вызывается вызовом yii\web\View::beginBody() в макетах.
  • EVENT_END_BODY: вызвано вызовом yii\web\View::endBody() в макетах.

Например, следующий код вводит текущую дату в конце тела страницы:

\Yii::$app->view->on(View::EVENT_END_BODY, function () {
    echo date('Y-m-d');
});

Рендеринг статических страниц

Статические страницы относятся к тем веб-страницам, основное содержание которых в основном статично, без необходимости обращения к динамическим данным, передаваемым с контроллеров.

Вы можете выводить статические страницы, помещая их код в представление, а затем используя следующий код в контроллере:

public function actionAbout()
{
    return $this->render('about');
}

Если веб-сайт содержит много статических страниц, было бы очень утомительно повторять подобный код много раз. Чтобы решить эту проблему, вы можете ввести автономное действие, называемое yii\web\ViewAction в контроллере. Например:

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, вы сможете отобразить это представление по следующему URL:

http://localhost/index.php?r=site%2Fpage&view=about

Вид параметров GET сообщает yii\web\ViewAction, какое представление запрашивается. Затем действие будет искать это представление в каталоге @app/views/site/pages. Вы можете настроить yii\web\ViewAction::$viewPrefix для изменения каталога для поиска этих представлений.