Представления

class Cake\View\View

Представления (Виды) - это то, что скрывается за буквой V в понятии MVC. Представления отвечают за вывод данных возвращаемых запросом в определенной форме. Часто это происходит в виде HTML, XML или JSON, но потоковые файлы и создание PDF-файлов, которые пользователи могут скачивать, также входят в круг обязанностей уровня Представления.

CakePHP поставляется с несколькими встроенными классами Представлений для обработки наиболее распростаненных сценариев рендеринга:

  • Для создания веб-сервисов использующих XML или JSON вы можете использовать представления JSON и XML.

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

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

Представление уровня приложения

AppView - это класс Представления по умолчанию вашего приложения. Сам по себе AppView наследуется от Cake\View\View, который включен в CakePHP, и определен в src/View/AppView.php следующим образом:

<?php
namespace App\View;

use Cake\View\View;

class AppView extends View
{
}

Вы можете использовать AppView для загрузки хелперов, которые будут использоваться для каждого вида, отображаемого в вашем приложении. CakePHP предоставляет метод initialize(), который вызывается в конце конструктора класса View для такого использования:

<?php
namespace App\View;

use Cake\View\View;

class AppView extends View
{

    public function initialize()
    {
        // Всегда активировать хелпер MyUtils
        $this->loadHelper('MyUtils');
    }

}

Шаблоны Представления

Слой представления CakePHP - это то, как вы общаетесь с вашими пользователями. Большую часть времени ваши представления будут выводить HTML/XHTML-документы в браузеры, но вам так же может понадобиться генерировать ответ удаленному клиентскому приложению посредством формаиа JSON, либо вывести пользователю CSV-файл.

Файлы шаблонов CakePHP имеют стандартное расширение .ctp (CakePHP Template) и используют альтернативный синтаксис управляющих структур PHP для управляющих структур и вывода. Эти файлы содержат логику, необходимую для подготовки данных, полученных от контроллера, в формат представления, подготовленный для вашей аудитории.

Варианты вывода значений переменных

C помщью стандартных чзыковых конструкций echo или print:

<?php echo $variable; ?>

С использованием сокращенного синтаксиса:

<?= $variable ?>

Альтернативные управляющие конструкции

Управляющие конструкции, такие как if, for, foreach, switch, и while могут быть записаны в упрощенном формате. Заметьте, что в этом случае не используются фигурные скобки. Вместо этого, к примеру для закрытия конструкции foreach закрывающая фигурная скобка заменяется на endforeach. Каждая из перечисленных выше управляющих конструкций, имеет схожий синтаксис закрытия: endif, endfor, endforeach, и endwhile.Также заметьте, что вместо использования точки с запятой после каждой структуры (за исключением последней) используется знак двоеточие.

Пример использования foreach:

<ul>
<?php foreach ($todo as $item): ?>
  <li><?= $item ?></li>
<?php endforeach; ?>
</ul>

Еще пример, использование if/elseif/else. Обратите внимание на двоеточия:

<?php if ($username === 'салли'): ?>
   <h3>Привет Салли</h3>
<?php elseif ($username === 'джо'): ?>
   <h3>Привет Джо</h3>
<?php else: ?>
   <h3>Привет неизвестный пользователь</h3>
<?php endif; ?>

Если же вы предпочтете использовать шаблонизаторы, подобные Twig, подкласс Представления будет посредничать между вашим шаблонизатором и CakePHP.

Файлы шаблонов хранятся в src/Template/ в папке с именем, соответствующим имени контроллера, использующего файлы, и имеют названия в честь экшенов, которые им соответствуют. Например, файл представления для экшена view() контроллера Products, обычно располагается по следующему пути - src/Template/Products/view.ctp.

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

  • шаблоны: Шаблоны - это часть страницы уникальная для текущего экшена. Они формируют основной костяк ответа на запрос в вашем приложении.

  • элементы: небольшая часть кода представления, которую можно использовать повторно. Элементы как правило выводятся внутри представлений.

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

  • хелперы: эти классы инкапсулируют логику представления, которая необходима во многих местах слоя представления. Наряду с другими вещами, хелперы в CakePHP могут помочь вам создать формы, построить AJAX-функциональность, разбить на страницы данные модели или транслировать RSS-каналы.

  • ячейки: эти классы предоставляют возможности схожие с функционалом контроллеров, но в меньшем масштабе, используемые при создании самодостаточных UI-компоненов. Для более подробной информации смотрите раздел Ячейки.

Переменные Представления

Любые переменные, заданные в вашем контроллере с помощью метода set(), будут доступны как в представлении, так и в макете вашего экшена. Кроме того, любые заданные переменные также будут доступны в любом элементе. Если вам нужно передать дополнительные переменные из представления в макет, вы можете либо вызвать set() в шаблоне представления, либо использовать блоки представления.

Помните, вы всегда должны экранировать любые пользовательские данные перед их выводом. Вы можете сделать это с помощью функции h():

<?= h($user->bio); ?>

Назначение переменных Представления

Cake\View\View::set(string $var, mixed $value)

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

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

$this->set('activeMenuButton', 'posts');

Тогда в вашем макете будет доступна переменная $activeMenuButton со значением „posts“.

Расширение Представлений

Расширение представлений дает вам возможность оборачивать одно представление в другое. Комбинирование этой возможности с блоками представления дает вам мощный способ для соблюдения принципа DRY. К примеру ваше приложение имеет сайдбар, который должен меняться в зависимости от конкретного отображаемого представления. Расширяя наиболее обобщенный файл представления, вы можете предотвратить повторение шаблонного кода для вашего сайдбара, и описывать только меняющиеся части:

<!-- src/Template/Common/view.ctp -->
<h1><?= $this->fetch('title') ?></h1>
<?= $this->fetch('content') ?>

<div class="actions">
    <h3>Related actions</h3>
    <ul>
    <?= $this->fetch('sidebar') ?>
    </ul>
</div>

Приведенный выше файл представления может быть использован в качестве родительского представления. Он ожидает, что представление, расширяющее его определит блоки sidebar и title. Блок content - это специальный блок, создаваемый CakePHP. Он будет содержать весь неохваченный контент из расширяющегося представления. Предположим, что в нашем файле представления имеется переменная $post, содержащая данные о нашем посте, в таком случае файл представления выглядел бы следующим образом:

<!-- src/Template/Posts/view.ctp -->
<?php
$this->extend('/Common/view');

$this->assign('title', $post->title);

$this->start('sidebar');
?>
<li>
<?php
echo $this->Html->link('edit', [
    'action' => 'edit',
    $post->id
]);
?>
</li>
<?php $this->end(); ?>

// Остальное содержимое будет доступно как блок 'content'
// в родительском представлении.
<?= h($post->body) ?>

Представление поста, приведенное выше, показывает, как вы можете расширить представление и заполнить набор блоков. Любое содержимое, которое не принадлежит определенному блоку, будет захвачено и помещено в специальный блок с именем content. Когда представление содержит обращение к методу extend(), выполнение продолжается до конца текущего файла представления. После завершения данного процесса, расширенный вид будет отображен. Вызов extend() более одного раза в файле представления будет переопределять родительское представление, которое будет обработано следующим:

$this->extend('/Common/view');
$this->extend('/Common/index');

В результате исполнения кода приведенного выше, для текущего представления родительским станет /Common/index.ctp.

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

Примечание

Вы должны избегать использование слова content в качестве имени блока в вашем приложении. CakePHP использует данное имя для вывода неохваченного содержимого в расширенных представлениях.

Вы можете получить перечень всех заполненных блоков с помощью метода blocks():

$list = $this->blocks();

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

Блоки представления предоставляют гибкий API, позволяющий вам определять блоки вашего представления/макета, которые будут определены где-либо еще. Например, блоки идеально подходят для реализации таких вещей, как сайдбар или области для загрузки контента в нижней/верхней части макета. Методы start(), append(), prepend(), assign(), fetch(), и end() позволяют работать с захватом блоков:

// Создание блока сайдбара.
$this->start('sidebar');
echo $this->element('sidebar/recent_topics');
echo $this->element('sidebar/recent_comments');
$this->end();

// Добавление новых блоков в конец сайдбара.
$this->start('sidebar');
echo $this->fetch('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();

Так же вы можете добавить новое содержимое в конец имеющегося блока с помощью метода append():

$this->append('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();

// То же самое что и в примере выше
$this->append('sidebar', $this->element('sidebar/popular_topics'));

Если вам нужно очистить или переписать блок, существует пара альтернатив. Метод reset() очистит или перепишет блок в любое время. Метод assign(), в который передается пустая строка также может быть использован в этом случае.:

// Очистить старое содержимое блока сайдбара.
$this->reset('sidebar');

// Присвоение пустой строки также очистит содержимое блока.
$this->assign('sidebar', '');

Добавлено в версии 3.2: Метод View::reset() был добавлен в версии 3.2

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

// В файле представления или макета выше строки $this->fetch('title')
$this->assign('title', $title);

Метод prepend() позволяет вам вставить содержимое перед существующим блоком:

// Добавить перед блоком сайдбара
$this->prepend('sidebar', 'это содержимое попадет в верхнюю часть сайдбара');

Отображение блоков

Вы можете выводить блоки, используя метод fetch(). Данный метод выведет блок, возвращающий „“ если указанный блок не будет существовать:

<?= $this->fetch('sidebar') ?>

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

// В макете src/Template/Layout/default.ctp
<?php if ($this->fetch('menu')): ?>
<div class="menu">
    <h3>Опции меню</h3>
    <?= $this->fetch('menu') ?>
</div>
<?php endif; ?>

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

<div class="shopping-cart">
    <h3>Your Cart</h3>
    <?= $this->fetch('cart', 'Your cart is empty') ?>
</div>

Использование блоков для скриптов и CSS-файлов

HtmlHelper связывается с блоками представления, а каждый из его методов script(), css() и meta() обновляет блок с соответствующим именем при использовании с параметром block = true:

<?php
// В вашем файле представления
$this->Html->script('carousel', ['block' => true]);
$this->Html->css('carousel', ['block' => true]);
?>

// В вашем файле макета.
<!DOCTYPE html>
<html lang="en">
    <head>
    <title><?= $this->fetch('title') ?></title>
    <?= $this->fetch('script') ?>
    <?= $this->fetch('css') ?>
    </head>
    // Остальная часть макета

Cake\View\Helper\HtmlHelper также позволяет вам контролировать в каких именно блоках будут размещены скрипты и файлы CSS:

// В вашем файле представления
$this->Html->script('carousel', ['block' => 'scriptBottom']);

// В вашем файле макета.
<?= $this->fetch('scriptBottom') ?>

Макеты

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

Макет, используемый в CakePHP по умолчанию располжен в файле src/Template/Layout/default.ctp. Если вы хотите полностью изменить внешний вид вашего приложения, это то место, с которого вам следует начать, так как выводимый контроллером код представлений располагается внутри этого стандартного макета при отображении страницы.

Все файлы макетов должны располагаться в папке src/Template/Layout. Когда вы создаете макет, вы должны указать, где CakePHP должен выводить ваши представления. Для этого выдолжны разместить в файле макета строку $this->fetch('content'). Вот пример того, как может выглядеть макет по умолчанию:

<!DOCTYPE html>
<html lang="en">
<head>
<title><?= h($this->fetch('title')) ?></title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<!-- Здесь подключаются внешние файлы и скрипты (См. HTML-хелпер для более полной информации.) -->
<?php
echo $this->fetch('meta');
echo $this->fetch('css');
echo $this->fetch('script');
?>
</head>
<body>

<!-- Если вы захотите, чтобы какое-нибудь меню
отображалось во всех ваших представлениях, добавьте его здесь -->
<div id="header">
    <div id="menu">...</div>
</div>

<!-- Место, в котором я хочу отображать свои представления -->
<?= $this->fetch('content') ?>

<!-- Добавляем футер к каждой странице -->
<div id="footer">...</div>

</body>
</html>

Блоки script, css и meta содержат любой контент, объявленный в представлениях с помощью встроенного HTML-хелпера. Это удобно для подключения JavaScript и CSS-файлов из представлений.

Примечание

При использовании HtmlHelper::css() или HtmlHelper::script() в шаблоне установите 'block' => true, чтобы расместить исходный HTML в одноименном блоке. (См. API чтобы узнать подробнее)

Блок content содержит контент выводимого представления.

Вы можете настроить блок title изнутри вашего файла представления:

$this->assign('title', 'View Active Users');

Вы можете создать столько макетов, сколько пожелаете: просто поместите их в папке src/Template/Layout и переключайтесь между ними в экшенах вашего контроллера, используя свойство $layout предствления или контроллера:

// Из контроллера
public function view()
{
    // Назначение макета.
    $this->viewBuilder()->setLayout('admin');

    // До версии 3.4
    $this->viewBuilder()->layout('admin');

    // До версии 3.1
    $this->layout = 'admin';
}

// Из файла представления
$this->layout = 'loggedin';

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

namespace App\Controller;

class UsersController extends AppController
{
    public function viewActive()
    {
        $this->set('title', 'View Active Users');
        $this->viewBuilder()->setLayout('default_small_ad');

        // в версиях ниже 3.4
        $this->viewBuilder()->layout('default_small_ad');

        // в версиях ниже 3.1
        $this->layout = 'default_small_ad';
    }

    public function viewImage()
    {
        $this->viewBuilder()->setLayout('image');

        // Вывод изображения пользователя
    }
}

Помимо стандартного макета, каркас приложения CakePHP также имеет макет „ajax“. Макет Ajax удобен для обработки ответов AJAX - это пустой макет. (Для большинства вызовов AJAX требуется только немного разметки взамен, а не полностью отображаемый интерфейс.)

Каркас приложения также включает стандартный макет для создания RSS.

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

Если вы хотите использовать макет, который находится внутри плагина, вы можете использовать plugin syntax. Например, чтобы использовать макет контакта из плагина Contacts:

namespace App\Controller;

class UsersController extends AppController
{
    public function view_active()
    {
        $this->viewBuilder()->layout('Contacts.contact');
        // или для версий ниже 3.1
        $this->layout = 'Contacts.contact';
    }
}

Элементы

Cake\View\View::element(string $elementPath, array $data, array $options = [])

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

Элементы находятся в папке src/Template/Element/, и имеют расширение .ctp. Они выводятся с помощью метода представления element():

echo $this->element('helpbox');

Передача переменных в элемент

Вы можете передавать данные в элемент, используя второй аргумент метода element():

echo $this->element('helpbox', [
    "helptext" => "О, этот текст очень полезен."
]);

Внутри файла элемента все переданные переменные дступны как элементы массива параметров (аналогично работе метода Controller::set() в контроллере, работающемс файлами шаблонов). В примере выше файл src/Template/Element/helpbox.ctp может использовать переменную $helptext:

// Внутри файла src/Template/Element/helpbox.ctp
echo $helptext; // Выводит "О, этот текст очень полезен."

Метод View::element() также поддерживает опции для элемента. Это опции „cache“ и „callbacks“. Пример:

echo $this->element('helpbox', [
        "helptext" => "Это передано в элемент как $helptext",
        "foobar" => "Это передано в элемент как $foobar",
    ],
    [
        // использует настройку кэша "long_view"
        "cache" => "long_view",
        // установлен в true, чтобы вызывались методы before/afterRender для элемента
        "callbacks" => true
    ]
);

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

$this->element('helpbox', [], [
        "cache" => ['config' => 'short', 'key' => 'уникальное значение']
    ]
);

Если вам требуется реализовать больше логики в вашем элементе, такие как динамически обновляемые данные, используйте для этого ячейки представления (View Cell) вместо элементов. Узнайте больше о Ячейках.

Кэширование элементов

Вы можете использовать преимущества кэширования представления CakePHP, если вы задаете параметр cache. Если установлено значение `` true``, он будет кэшировать элемент в конфигурации кэша „по умолчанию“. В противном случае вы можете назначить, какую конфигурацию кэша следует использовать. Смотрите Кэширование для получения дополнительной информации о настройке Cache. Простым примером кэширования элемента будет:

echo $this->element('helpbox', [], ['cache' => true]);

Если вы выводите один и тот же элемент более одного раза в представлении при включенном кешировании, обязательно устанавливайте параметр „key“ в другое значение каждый раз. Это предотвратит переопределение кэшированных результатов при каждом новом вызове метода element(). Например:

echo $this->element(
    'helpbox',
    ['var' => $var],
    ['cache' => ['key' => 'first_use', 'config' => 'view_long']]
);

echo $this->element(
    'helpbox',
    ['var' => $differenVar],
    ['cache' => ['key' => 'second_use', 'config' => 'view_long']]
);

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

Запрос элементов из плагина

Если вы используете плагин и хотите использовать элементы из плагина, просто используйте знакомый plugin syntax. Если представление создается для контроллера/экшена плагина, имя плагина будет автоматически ставиться перед всеми используемыми элементами до тех пор, пока не будет явно указано название другого плагина. Если нужный элемент отсутствует в плагине, он будет искаться в папке основного приложения:

echo $this->element('Contacts.helpbox');

Если ваше представление - это часть плагина, вы можете не указывать явно имя плагина. Например, если вы находитесь в контроллере ContactsController плагина Contacts, то следующие строки:

echo $this->element('helpbox');
// и
echo $this->element('Contacts.helpbox');

будут равнозначны по смыслу и выведут один и тот же элемент.

Для элементов внутри вложенных папок плагина (например, plugins/Contacts/Template/Element/sidebar/helpbox.ctp), используйте следующий синтаксис:

echo $this->element('Contacts.sidebar/helpbox');

Префикс маршрута и элементы

Добавлено в версии 3.0.1.

Если у вас в маршруте используется префикс, область видимости пути Элемента может переключиться на местоположение соответствующее префиксу, так же как это делают Макеты и Представления. Предположим у вас настроен префикс «Admin», и вы делаете следующий вызов:

echo $this->element('my_element');

Сначала элемент будет искаться в src/Template/Admin/Element/. Если его не окажется в той папке, то поиск продолжится по стандартному пути.

Кэширование секций вашего представления

Cake\View\View::cache(callable $block, array $options = [])

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

// Предполагаются некоторые локальные переменные
echo $this->cache(function () use ($user, $article) {
    echo $this->cell('UserProfile', [$user]);
    echo $this->cell('ArticleFull', [$article]);
}, ['key' => 'my_view_key']);

По умолчанию кэшированное содержимое представления будет передаваться в параметр конфигурации View::$elementCache, но вы можете использовать опцию config, чтобы изменить это.

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

Так же, как и Контроллер, представление запускает несколько коллбэков/событий, которыми вы можете пользоваться для встраивания дополнительной логики вокруг жизненного цикла отображения данных:

Список событий

  • View.beforeRender

  • View.beforeRenderFile

  • View.afterRenderFile

  • View.afterRender

  • View.beforeLayout

  • View.afterLayout

Вы можете прикреплять обработчики приложения к указанным событиям, или использовать Коллбэки Хелпера.

Создание ваших собственных классов представления

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

  • Файлы классов представления должны быть расположены в папке src/View. Например: src/View/PdfView.php

  • Имена классов представления должны оканчиваться на View. Например: PdfView.

  • Когда вы ссылаетесь имена классов представления, вы должны опускать суффикс View. Например: $this->viewBuilder()->className('Pdf');.

Также, для того, чтобы все работало правильно, вы должны убдиться, что наследуетесь от класса View:

// В src/View/PdfView.php
namespace App\View;

use Cake\View\View;

class PdfView extends View
{
    public function render($view = null, $layout = null)
    {
        // Здесь находится ваша логика.
    }
}

Переопределение метода render() позволяет вам полностью контролировать то, как отображается ваш контент.