Ячейки Представления

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

Когда использовать ячейки

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

Создание ячеек

Чтобы создать ячейку, определите класс в src/View/Cell и шаблон в src/Template/Cell/. В этом примере мы создадим ячейку для отображения количества сообщений в почтовом ящике уведомлений пользователя. Сначала создайте файл класса. Его содержимое должно выглядеть так:

namespace App\View\Cell;

use Cake\View\Cell;

class InboxCell extends Cell
{

    public function display()
    {
    }

}

Сохраните этот файл как src/View/Cell/InboxCell.php. Как вы можете заметить, подобно другим классам в CakePHP, Ячейки имеют несколько соглашений:

  • Ячейки располагаются в пространстве имен App\View\Cell. Если вы создаете ячейку в плагине, пространство имен должно быть вида PluginName\View\Cell.
  • Имена классов должны оканчиваться на Cell.
  • Классы должны наследоваться от Cake\View\Cell.

Мы добавили пустой метод display() в нашу ячейку; это традиционный метод по умолчанию при рендеринге ячейки. Мы рассмотрим, как использовать другие методы позже в документации. Теперь создайте файл src/Template/Cell/Inbox/display.ctp. Это будет наш шаблон для нашей новой ячейки.

Мы можем быстро сгенерировать эту заготовку кода, используя bake:

bin/cake bake cell Inbox

Это создаст код, аналогичный тому, что мы описали выше.

Реализация Ячеек

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

namespace App\View\Cell;

use Cake\View\Cell;

class InboxCell extends Cell
{

    public function display()
    {
        $this->loadModel('Messages');
        $unread = $this->Messages->find('unread');
        $this->set('unread_count', $unread->count());
    }

}

Поскольку ячейки используют ModelAwareTrait и ViewVarsTrait, их поведение очень схоже с поведением контроллера. Мы можем использовать методы loadModel() и set() точно так же, как в контроллере. В нашем файле шаблона добавьте следующее:

<!-- src/Template/Cell/Inbox/display.ctp -->
<div class="notification-icon">
    У вас <?= $unread_count ?> непрочитанных сообщений.
</div>

Примечание

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

Загрузка ячеек

Ячейки могут быть загружены из представлений с помощью метода cell(), и работают аналогично в обоих контекстах:

// Загрузка ячейки на уровне приложения
$cell = $this->cell('Inbox');

// Загрузка ячейки из плагина
$cell = $this->cell('Messaging.Inbox');

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

// Выполнить метод expanded() ячейки Inbox
$cell = $this->cell('Inbox::expanded');

Если вам понадобится логика контроллера, чтобы решить какие ячейки загружать в запросе, вы можете использовать CellTrait в вашем контроллере, чтобы сделать доступным метод cell():

namespace App\Controller;

use App\Controller\AppController;
use Cake\View\CellTrait;

class DashboardsController extends AppController
{
    use CellTrait;

    // Остальной код.
}

Передача аргументов ячейке

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

$cell = $this->cell('Inbox::recent', ['-3 days']);

Это будет соответствовавть следующей сигнатуре функции:

public function recent($since)
{
}

Отображение ячейки

Как только ячейка будет загружена и выполнена, вы вероятно захотите отобразить её. Простейший способ отобразить ячейку - использовать echo:

<?= $cell ?>

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

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

Примечание

Вывод ячейки с помощью echo использует магический метод PHP __toString(), который предотвращает вывод имени файла и номера строки для появляющихся ошибок. Для получения более информативных сообщений об ошибках рекомендуется использовать метод Cell::render(), например

Отображение альтернативных шаблонов

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

// Явный вызов метода render()
echo $this->cell('Inbox::recent', ['-3 days'])->render('messages');

// Установка шаблона перед выводом ячейки.
$cell = $this->cell('Inbox');
$cell->viewBuilder()->setTemplate('messages');
// До версии 3.4
$cell->viewBuilder()->template('messages');
// До версии 3.1
$cell->template = 'messages';
echo $cell;

Кэширование выводимых в ячейках данных

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

// Кэширование с использованием стандартной конфигурации и со сгенерированным ключом
$cell = $this->cell('Inbox', [], ['cache' => true]);

// Кэширование с определенной конфигурацией и со сгенерированным ключом
$cell = $this->cell('Inbox', [], ['cache' => ['config' => 'cell_cache']]);

// Определены и ключ и конфигурация кэширования
$cell = $this->cell('Inbox', [], [
    'cache' => ['config' => 'cell_cache', 'key' => 'inbox_' . $user->id]
]);

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

Примечание

Для обработки каждой ячейки используется новый экземпляр View, таким образом они не используют общий контекст с главным шаблоном/макетом. Каждая ячейка изолирована, и может иметь доступ только к переменным, переданным в качестве параметров при вызове метода View::cell().

Постраничная навигация внутри ячеек

Создание ячейки, которая выводит разбитый на страницы результат запроса, может быть осуществлено с помощью класса ORM Paginator. Пример постраничной навигации по избранным сообщениям пользователя может выглядеть так:

namespace App\View\Cell;

use Cake\View\Cell;
use Cake\Datasource\Paginator;

class FavoritesCell extends Cell
{
    public function display($user)
    {
        $this->loadModel('Messages');

        // Создание пагинатора
        $paginator = new Paginator();

        // Разбиение модели на страницы
        $results = $paginator->paginate(
            $this->Messages,
            $this->request->getQueryParams(),
            [
                // Use a parameterized custom finder.
                'finder' => ['favorites' => [$user]],

                // Use scoped query string parameters.
                'scope' => 'favorites',
            ]
        );

        $paging = $paginator->getPagingParams() + (array)$request->getParam('paging');
        $this->request = $this->request->withParam('paging', $paging));

        $this->set('favorites', $results);
    }
}

Описанная выше ячейка разбивает на страницы модель Messages, используя scoped pagination parameters.

Добавлено в версии 3.5.0: Cake\Datasource\Paginator был добавлен в версии 3.5.0.

Параметры ячеек

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

namespace App\View\Cell;

use Cake\View\Cell;
use Cake\Datasource\Paginator;

class FavoritesCell extends Cell
{
    protected $_validCellOptions = ['limit'];

    protected $limit = 3;

    public function display($userId)
    {
        $this->loadModel('Users');
        $result = $this->Users->find('friends', ['for' => $userId]);
        $this->set('favorites', $result);
    }
}

Здесь мы определили свойство $limit и добавили параметр ячейки limit. Это позволит нам определять параметр при создании ячейки:

$cell = $this->cell('Favorites', [$user->id], ['limit' => 10])

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