Контроллеры. Пакет main. Выводим анонсы записей в блоге, делаем пагинацию
Оглавление
Контроллеры
Сначала немного общей информации.
Определение
Контроллер - это класс, который обрабатывает входящие запросы и отдаёт данные в представление. Что такое представление? Для нас - всё, что лежит в папке views.
Связь осуществляется через поле шаблона псевдоним. Да, опять оно. Важное поле в Evolution CMS.
Примеры
Для шаблона с псевдонимом main соответствующее название контроллера MainController.php
Для шаблона с псевдонимом news_main соответствующее название контроллера NewsMainController.php
Символ подчёркивания вырезается. В псевдониме используйте только буквы и подчёркивание.
Пакет Main
Для того, чтобы полноценно работать с контроллерами в Evolution CMS, нужно совершить несколько действий.
Команда package:create
Для начала открываем консоль сервера и переходим в папку сайта. Дальше идём в папку core.

Теперь обновим существующие пакеты, чтобы не было ошибок. Пишем команду:
composer update
Операция длительная - менеджер зависимостей composer проверяет, что у нас установлено и при необходимости скачивает и обновляет пакеты и их зависимости.
Теперь необходимо создать некий “стартовый” пакет, где мы будем писать контроллеры. Как правило, этот пакет называют main. Введите команду:
php artisan package:create main
Перейдите в папку /core/custom/packages/. Внутри вы должны увидеть папку main с разными вложенными папками и файлами.

На данном этапе все они нас не особо интересуют, кроме папки /core/custom/packages/main/src/Controllers.
Именно здесь будут расположены контроллеры. В дальнейшем я буду говорить о папке Controllers как о папке внутри пакета main, чтобы не писать каждый раз полный путь.
Указываем namespace
Теперь нужно указать Evolution CMS область имен, откуда она будет брать контроллеры. Это делается в системной настройке Controller Namespace.
Можно указать её в настройках сайта в админ-панели, либо вынести в файл settings.php (третий урок). Мы сделаем второй вариант.
Ищем файл core\custom\config\cms\settings.php.
Добавляем настройку:
<?php
return [
/*
тут у вас уже есть другие параметры
*/
'ControllerNamespace' => 'EvolutionCMS\\Main\\Controllers\\'
];
Двойные слеши нужны для экранирования. Обратите внимание, что этот namespace у нас будет во всех контроллерах в папке core/custom/packages/main/src/Controllers/.
Пример:
<?php
namespace EvolutionCMS\Main\Controllers;
Почему? Потому что имя пакета main. Для пакета с другим именем замените Main на ваше наименование.
Базовый контроллер
Особенность работы с контроллерами в Evolution CMS такова, что нужно создать BaseController - некий базовый контроллер “для всего”.
Он будет использоваться по умолчанию, если вы забыли/не захотели указать контроллер для определённого шаблона.
Давайте пока что сделаем его пустым, как некую “заглушку”. Создайте файл Controllers/BaseController.php:
<?php
namespace EvolutionCMS\Main\Controllers;
class BaseController{
public function __construct() {
//
}
}
Вот и всё. В дальнейшем мы к нему вернёмся.
Теперь нужно сделать контроллеры для шаблонов.
Отладка и Tracy
Чтобы нам было проще с контроллерами, давайте включим Tracy. Это удобная панель для отображения различных данных страницы.
Создайте папку
core/custom/config/tracy/
Создайте файл active.php
<?php
return 'adminfrontonly';
Чуть ниже мы воспользуемся Tracy для просмотра данных.
Контроллер шаблона
Главная страница
Давайте сделаем ленту постов на главной странице.
Амбула: в шаблонах откуда-то надо брать данные и это “откуда-то” как раз и есть связь шаблона и контроллера.
Вспоминаем псевдонимы.
| Имя | Псевдоним | Описание |
|---|---|---|
| Главная страница | main | Для главной |
| Все блоги | blogs | Лента блогов |
| Пост в блоге | post | Пост в блоге |
| Контакты | contacts | Контакты |
| Все теги | tags | Лента тегов |
| Тег | tag | Страница тега |
У шаблона главной страницы псевдоним main. Значит, создаём файл Controllers/MainController.php:
<?php
namespace EvolutionCMS\Main\Controllers;
class MainController{
public function __construct() {
dd('main');
}
}
Функция
dd()означает dump & die - выведи и умри, если буквально. Она выводит любые переменные и объекты на печать.
Откройте главную страницу сайта, там должно быть напечатано слово main.

Что может пойти не так: неверное имя контроллера, неверное местоположение файла, неверное название класса.
Как отдать данные во view?
В любом контроллере доступен объект EvolutionCMS(). Среди прочих в нем имеется метод addDataToView, который как раз отдаёт данные в шаблон.
Пишем:
<?php
namespace EvolutionCMS\Main\Controllers;
class MainController {
public $data = [];
public $evo = null;
public function __construct() {
$this->evo = EvolutionCMS();
$this->render();
$this->sendToView();
}
public function render()
{
$this->data['anykey'] = 'main controller data';
return $this;
}
public function sendToView()
{
$this->evo->addDataToView($this->data);
return $this;
}
}
Что происходит?
Сделана переменная $data, где мы будем хранить нужные нам данные.
- Метод
sendToViewотдаёт данные$dataв шаблон. - Метод
renderдля присваивания данных. Просто, чтобы не писать “простыни” в__construct. - Там мы добавили переменной
$dataключanykeyи текстовое значение для теста. - В методе
__construct(который выполняется по умолчанию) мы присваиваем переменной$evoзначениеEvolutionCMS(), чтобы достучаться до системных функций. - Дальше вызываем по очереди оба метода, чтобы присвоить значения и отдать их в шаблон.
Откройте главную страницу и обратите внимание на нижнюю часть. Это и есть панель Tracy.
Во вкладке View можете увидеть данные, которые в нашем распоряжении. В частности, свежесозданный ключ anykey и его значение.

Откройте шаблон главной страницы и для теста выведите значение.
{{ $anykey }}

Используем DocLister
Для создания лент ресурсов есть много способов, но самый простой на данном этапе - использование сниппета DocLister. Мы установили его ранее. Так что теперь просто воспользуемся им в контроллере при помощи метода Evolution CMS runSnippet.
Сниппет - кусок самостоятельного кода, который реализует любую функциональность, доступную в php. Может быть как написан вами, так и скачан в рамках готовых дополнений (тот же DocLister).
Подробности про сниппеты вы можете найти в документации. Пока достаточно знать, что DocLister специализируется как раз на выборке различных лент материалов, фильтрации и обработке.
Метод runSnippet запускает сниппет из php.
Давайте применим эти знания для выбора постов.
<?php
namespace EvolutionCMS\Main\Controllers;
class MainController {
public $data = [];
public $evo = null;
public function __construct() {
$this->evo = EvolutionCMS();
$this->render();
$this->sendToView();
}
public function render()
{
$result = $this->evo->runSnippet('DocLister',[
'parents' => 2,
'depth' => 1,
'tvPrefix' => '',
'tvList' => 'post_mainphoto',
'display' => 10,
'returnDLObject' => 1,
]);
$this->data['posts'] = $result->getDocs();
return $this;
}
public function sendToView()
{
$this->evo->addDataToView($this->data);
return $this;
}
}
Доклистеру переданы параметры, самый важный из которых returnDLObject - просьба вернуть объект, а не отдавать сразу результаты. В противном случае мы получили бы свёрстанный html. Параметр parents указывает, откуда выбирать посты. Параметр depth - глубину выборки, а display - количестов постов.
Параметры tvList и tvPrefix относятся к выбору ТВ-параметров - по умолчанию Доклистер не выбирает ТВ документа, чтобы сэкономить на mysql-запросах. Так что все нужные ТВ необходимо перечислить в tvList.
Итак, у нас есть переменная $posts, в которой массив выбранных документов. Эту переменную метод sendToView отдаёт шаблону.
Теперь мы можем оперировать данными уже в шаблоне.
Цикл foreach
Заходим в шаблон главной страницы main.blade.php и правим его:
@extends('layouts.app')
@section('content')
@foreach ($posts as $post)
<article class="post">
<header>
<div class="title">
<h1>
<a href="{{ urlProcessor::makeUrl($post['id'] ) }}">{{ $post['pagetitle'] }}</a>
</h1>
<a href="{{ urlProcessor::makeUrl($post['id'] ) }}" class="image featured">
<img src=" {{ $post['post_mainphoto'] }}" alt="" />
</a>
</div>
</header>
{{ $post['introtext'] }}
<footer>
<ul class="stats">
<li><a href="/tags/afrika" class="icon solid fa-tag">Африка</a></li>
<li><a href="#" class="icon solid fa-heart">28</a></li>
<li><a href="#" class="icon solid fa-comment">128</a></li>
</ul>
</footer>
</article>
@endforeach
@endsection
Обратите внимание на @foreach - это цикл по массиву постов $posts, которые мы отдали в шаблон. Внутри цикла мы просто получаем доступ к значениям каждого поста и выводим их в нужных местах.
Внимания заслуживает и конструкция urlProcessor.
{{ urlProcessor::makeUrl($post['id'] ) }}
Это директива EvolutionCMS. Служит для создания ссылки на документ. В неё мы просто передали id поста.
Запомним:
{{ urlProcessor::makeUrl( идентификатор ) }}сделает нам ссылку
Таким образом, вы должны увидеть на главной странице сверстанный список всех постов.
Секцию footer поправим позже, пусть остаётся пока что статичной.
Наследование контроллера
Контроллер страницы блогов
На главной странице нас всё устраивает. А вот для страницы “Блоги” нужна пагинация, ведь, когда постов будут сотни, нельзя их показывать разом на одной странице.
Делаем контроллер для шаблона “Все блоги”. Его псевдоним blogs, значит файл будет называться BlogsController.php.
Наследование. Давайте рассуждать
В контроллере блогов BlogsController методы из контроллера главной страницы MainController повторяются. Повторяются переменные, логика. Вызов методов абсолютно такой же.
А что, если нам вынести все нужные методы в базовый класс, унаследовать его и потом спокойно пользоваться в контроллерах шаблона?
А что, если нам понадобится отдать какие-то данные сразу всем шаблонам? А что, если что-то ещё?
Наследование. Давайте делать
Открываем BaseController и изменяем его:
<?php
namespace EvolutionCMS\Main\Controllers;
class BaseController {
public $data = [];
public $evo = null;
public function __construct() {
$this->evo = EvolutionCMS();
$this->render();
$this->sendToView();
}
public function render()
{
return $this;
}
public function sendToView()
{
$this->evo->addDataToView($this->data);
return $this;
}
}
А в MainController и BlogsController наследуем BaseController и убираем ненужное.
<?php
namespace EvolutionCMS\Main\Controllers;
class MainController extends BaseController {
public function render()
{
$result = $this->evo->runSnippet('DocLister',[
'parents' => 2,
'depth' => 1,
'tvPrefix' => '',
'tvList' => 'post_mainphoto',
'display' => 10,
'returnDLObject' => 1,
]);
$this->data['posts'] = $result->getDocs();
return $this;
}
}
Обратите внимание - extends BaseController
Как это работает?
- Мы убрали все методы, кроме
render(). - За выдачу результата шаблону отвечает по-прежнему метод
sendToView, расположенный уже в extended контроллереBaseController. - В
MainControllerего писать нет смысла, как нет смысла и в методе__construct. Нам важно только одно - присвоить какому-то ключу переменной$dataнужные значения. Остальное сделаетBaseController.
Это удобно - в случае каких-то глобальных доработок мы можем поправить только BaseController, остальные унаследуют всю его функциональность.
Перейдём к контроллеру блогов BlogsController.
<?php
namespace EvolutionCMS\Main\Controllers;
class BlogsController extends BaseController {
public function render()
{
$result = $this->evo->runSnippet('DocLister',[
'parents' => 2,
'depth' => 1,
'tvPrefix' => '',
'tvList' => 'post_mainphoto',
'returnDLObject' => 1,
'paginate' => 'pages',
'display' => 10,
'TplPrevP' => '@CODE: <li><a href="[+link+]" class="button previous">«</a></li>',
'TplNextP' => '@CODE: <li><a href="[+link+]" class="button next">»</a></li>',
'TplPage' => '@CODE: <li><a class="button" href="[+link+]">[+num+]</a></li>',
'TplCurrentPage' => '@CODE: <li class=" is-active">[+num+]</li>',
'TplWrapPaginate'=>'@CODE: <ul class="actions special pagination">[+wrap+]</ul>',
]);
$this->data['posts'] = $result->getDocs();
return $this;
}
}
Изменились только параметры вызова Доклистера. Это документированные опции, изучите документацию Доклистер. Серьёзно, Доклистер - база для многих вещей в Evolution CMS. Всё, что делают эти опции - это добавляют пагинацию и её разметку.
Пагинация
Теперь нужно вывести сами посты и пагинацию в шаблоне blogs.blade.php
@extends('layouts.app')
@section('content')
@foreach ($posts as $post)
<article class="post">
<header>
<div class="title">
<h1>
<a href="{{ urlProcessor::makeUrl($post['id'] ) }}">{{ $post['pagetitle'] }}</a>
</h1>
<a href="{{ urlProcessor::makeUrl($post['id'] ) }}" class="image featured">
<img src="{{ $post['post_mainphoto'] }}" alt="" />
</a>
</div>
</header>
{{ $post['introtext'] }}
<footer>
<ul class="stats">
<li><a href="/tags/afrika" class="icon solid fa-tag">Африка</a></li>
<li><a href="#" class="icon solid fa-heart">28</a></li>
<li><a href="#" class="icon solid fa-comment">128</a></li>
</ul>
</footer>
</article>
@endforeach
{!! $modx->getPlaceholder('pages') !!}
@endsection
Добавилось одно новое выражение {!! $modx->getPlaceholder('pages') !!}. Это вызов пагинации Доклистера. Остальная разметка и директивы 1 в 1 повторяют шаблон для главной.
В Evolution CMS есть такая сущность - плейсхолдер. Это переменная, доступная всегда и везде. Доклистер засовывает в плейсхолдер pages пагинацию, а метод getPlaceholder её достаёт оттуда. Вот и всё. Подробности в документации Доклистера.

Вот как должна выглядеть пагинация.
Что может пойти не так? Если у вас меньше 10 постов в блоге, разумеется, пагинации не будет. Либо добавьте содержимое, либо уменьшите количестов постов в параметре display.
Итого
- В посте есть содержимое
- Работает лента постов на главной
- Работает лента постов на странице “Блоги”. В ней есть пагинация
- По клику мы можем перейти внутрь поста
- Вы запомнили базовую информацию про контроллеры
- Первый раз использовали Доклистер.
Мне надоело пользоваться кнопкой “Просмотр” в админке, чтобы перейти куда-то, давайте сделаем навигационное меню на сайте.