XTemplate и Controllers в Cotonti: Руководство для разработчиков

XTemplate - инструмент в Cotonti CMF для отделения HTML-вёрстки от PHP-логики. Инструкция для разработчиков плагинов и модулей, которые хотят глубоко понять работу, научиться создавать и модифицировать шаблоны своих расширений

Рейтинг на основе отзывов:
Звезд получено: 0
Oтзывов всего: 0
Средняя оценка: 0
Опубликовано в: Блог пользователей

XTemplate и хуки в Cotonti: руководство разработчика

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

Это руководство построено от общего к частному и опирается только на реальные механизмы Cotonti (включая Verona 1.0.x). Каждый раздел содержит примеры, которые можно сразу использовать в своих проектах.

Часть I. XTemplate — классический шаблонизатор Cotonti

1.1. Роль в архитектуре

XTemplate реализует слой «View» в модели MVC, хотя Cotonti не навязывает жёсткого разделения. Он интерпретирует шаблоны во время выполнения, не компилируя их в PHP-код. Такой подход обеспечивает простоту, но при очень высоких нагрузках может потребовать кеширования на уровне контроллеров.

1.2. Создание объекта XTemplate

Для начала работы с шаблоном создаётся экземпляр класса XTemplate (расположен в system/xtemplate.php). Путь к файлу определяется функцией cot_tplfile():

$t = new XTemplate(cot_tplfile('mymodule', 'plug'));

Первый аргумент — имя файла без расширения, второй — тип: 'plug' для плагинов, 'module' для модулей, 'core' для системных нужд. Система автоматически ищет переопределённый файл в теме, затем в самом расширении.

1.3. Теги и assign()

Теги заключаются в фигурные скобки: {MY_TAG}. Они регистрозависимы. Значения назначаются методом assign(), который принимает строку или ассоциативный массив:

$t->assign('TITLE', 'Заголовок');
$t->assign([
    'TITLE'   => $title,
    'CONTENT' => $text,
    'IS_ADMIN' => Cot::$usr['isadmin']
]);

Теги без значений при парсинге удаляются из вывода — это помогает избежать пустых HTML-конструкций.

1.4. Блоки и parse()

Блоки ограничены комментариями <!-- BEGIN: NAME --> и <!-- END: NAME -->. Внешний блок обычно называется MAIN и парсится автоматически. Вложенные блоки необходимо парсить вручную с указанием родителя через точку: $t->parse('MAIN.PAGE_ROW').

Пример шаблона mymodule.tpl:

<!-- BEGIN: MAIN -->
<h1>{PAGE_TITLE}</h1>
<table>
  <!-- BEGIN: PAGE_ROW -->
  <tr>
      <td>{PAGE_ROW_ID}</td>
      <td>{PAGE_ROW_TITLE}</td>
  </tr>
  <!-- END: PAGE_ROW -->
</table>
<!-- END: MAIN -->

PHP-код для заполнения данными из базы:

$t = new XTemplate(cot_tplfile('mymodule', 'plug'));
$t->assign('PAGE_TITLE', 'Список страниц');

$res = Cot::$db->query("SELECT page_id, page_title FROM $db_pages WHERE page_state = 0");
while ($row = $res->fetch()) {
    $t->assign([
        'PAGE_ROW_ID'    => $row['page_id'],
        'PAGE_ROW_TITLE' => htmlspecialchars($row['page_title'])
    ]);
    $t->parse('MAIN.PAGE_ROW');   // немедленный разбор текущей строки
}
$t->parse('MAIN');
echo $t->text('MAIN');

Вызов parse() внутри цикла заставляет XTemplate сразу формировать вывод для каждой итерации. Если бы он вызывался только в конце, все теги содержали бы значения последней строки.

1.5. Типы данных и условные операторы

assign() можно передавать числа, булевы значения. Условные конструкции обрабатываются на этапе рендеринга через <!-- IF {УСЛОВИЕ} --> ... <!-- ENDIF -->. Условия — это обычные PHP-выражения, обрамлённые фигурными скобками.

Пример передачи флага администратора:

$t->assign('IS_ADMIN', Cot::$usr['isadmin']);

В шаблоне:

<!-- IF {IS_ADMIN} -->
<a href="{ADMIN_EDIT_URL}">Редактировать</a>
<!-- ENDIF -->

1.6. Модификаторы (фильтры)

XTemplate поддерживает фильтры, вызываемые через вертикальную черту: {TAG|filter:param}. Встроенные фильтры: cot_date('format', $value), strip_tags($value), htmlspecialchars($value), а также большинство стандартных функций PHP, разрешённых конфигурацией.

Собственные фильтры регистрируются через хук coTemplate.custom.callback. Пример регистрации myuppercase:

// в файле обработчика хука coTemplate.custom.callback
$callbacks['myuppercase'] = function ($value, $args) {
    return mb_strtoupper($value, 'UTF-8');
};
// $callbacks передаётся по ссылке, нужно вернуть изменённый массив

После этого в любом шаблоне можно писать {SOME_VAR|myuppercase}.

1.7. XTemplate и контроллеры

С появлением контроллеров в Cotonti (ветка Siena, Verona) XTemplate остался основным движком рендеринга. Контроллер возвращает строку, которая становится телом ответа. Типичное действие:

namespace cot\modules\pages\controllers;

use cot\controllers\BaseController;

class PageController extends BaseController
{
    public function actionList(): string
    {
        $t = new XTemplate(cot_tplfile('pages.list', 'module'));
        // ... assign, parse ...
        $t->parse('MAIN');
        return $t->text('MAIN');
    }
}

Функция cot_tplfile() с типом 'module' ищет шаблон сначала в теме (themes/имя_темы/modules/...), затем в modules/имя_модуля/tpl/.

1.8. Пагинация

Для пагинации используется связка cot_pagenav() и cot_generatePaginationTags():

$pagenav = cot_pagenav('pages', $url_params, $d, $total, $perpage);
$t->assign(cot_generatePaginationTags($pagenav));

В шаблоне доступны {PAGENAV_PAGES}, {PAGENAV_PREV}, {PAGENAV_NEXT} и т.д.

Часть II. Система хуков в Cotonti

2.1. Принцип работы

Хук — это точка в коде, в которую Cotonti подключает файлы-обработчики, зарегистрированные расширениями. Подключение происходит через include, что даёт обработчику доступ ко всем переменным текущей области видимости.

Пример вызова хука из ядра:

/* === Hook === */
foreach (cot_getextplugins('page.tags') as $pl) {
    include $pl;
}
/* ============= */

Функция cot_getextplugins() возвращает массив полных путей к файлам обработчиков, зарегистрированных на указанный хук, с учётом их приоритета (параметр Order в заголовке).

2.2. Регистрация обработчика

Обработчик — это PHP-файл в папке расширения с особым заголовком. Например, файл myext.page.edit.update.done.php может содержать:

<?php
/* ====================
[BEGIN_COT_EXT]
Hooks=page.edit.update.done
Order=10
[END_COT_EXT]
==================== */

defined('COT_CODE') or die('Wrong URL');

// Код обработчика
  • Hooks — обязательное поле, список хуков через запятую, на которые реагирует файл.
  • Order — порядок вызова (1–99). По умолчанию 10. Меньшее число — более ранний вызов.
  • Tags — опциональное описание добавляемых тегов, видимое в админке.

Один файл может обслуживать несколько хуков, если указать их через запятую: Hooks=page.tags,index.tags.

2.3. Основные системные хуки

Существует ряд хуков, вызываемых почти при каждом запросе:

  • input — первая точка расширения, система ещё полностью не инициализирована. Используется для раннего переопределения данных.
  • rc (не AJAX) — для загрузки ресурсов, модификации тем/языков.
  • global — сразу после инициализации системы.
  • header.first / header.main / header.tags — перед выводом заголовка страницы.
  • footer.first / footer.main / footer.tags / footer.last — аналогично для подвала.
  • output — последний хук перед отправкой буфера вывода.

Для отдельных страниц используются:

  • standalone — точка входа для плагинов.
  • module — для модулей.
  • admin — для модулей админки.
  • tools — для плагинов в разделе админки «Прочее».
  • ajax — при AJAX-запросах.

Внутрифункциональные хуки: die.message, structure, parser.last, usertags.first, usertags.main и другие. Их вызов привязан к конкретным функциям ядра.

2.4. Циклические хуки

Хуки, оканчивающиеся на .loop, вызываются многократно внутри цикла (например, при обходе строк SQL-запроса). Чтобы не нагружать систему повторным поиском обработчиков, массив извлекается до цикла:

$extp = cot_getextplugins('myext.some.loop');
foreach ($items as $item) {
    /* === Hook === */
    foreach ($extp as $pl) {
        include $pl;
    }
    /* ============= */
}

2.5. Область видимости и класс Cot

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

Начиная с версии 0.9.15, все основные глобальные переменные доступны через статические свойства класса Cot. Пример:

if (Cot::$cfg['plugin']['comments']['enabled']) {
    // ...
}

Полный список переменных, доступных через Cot::$: $cache, $cfg, $cot_extrafields, $db, $db_x, $env, $L, $out, $R, $structure, $sys, $usr. Использование Cot::$ предпочтительнее, так как не требует global и одинаково работает в любых областях видимости.

Часть III. Глобальные переменные и их использование в шаблонах

3.1. Доступ через CoTemplate

Любая глобальная переменная может быть использована в шаблоне через синтаксис {PHP.имя_переменной}. Например, {PHP.usr.id} или {PHP.cfg.mainurl}. Это позволяет выводить системные данные без дополнительных assign() в PHP.

3.2. Основные глобальные переменные

$usr — данные текущего пользователя:

  • $usr['id'] — ID (0 для гостя)
  • $usr['name'] — логин
  • $usr['maingrp'] — основная группа
  • $usr['profile']['user_...'] — все поля профиля, включая экстраполя

$cfg — конфигурация:

  • $cfg['option'] — настройки ядра
  • $cfg['module_name']['option'] — настройки модуля
  • $cfg['plugin']['plugin_name']['option'] — настройки плагина

$env — окружение запроса:

  • $env['ext'] — код текущего расширения
  • $env['type'] — 'module' или 'plug'
  • $env['status'] — HTTP-статус (например, '200 OK')

$sys — системные переменные:

  • $sys['now'] — текущее Unix-время
  • $sys['xk'] — CSRF-токен
  • $sys['abs_url'] — абсолютный URL сайта

$out — выходные данные:

  • $out['subtitle'] — заголовок страницы (тег <title>)
  • $out['meta_desc'] — мета-описание
  • $out['meta_keywords'] — ключевые слова

$structure — дерево категорий:

  • $structure['module_name']['category_code'] — массив с ключами title, desc, icon, count и др.

$db — объект базы данных (PDO-обёртка).

$cache — объект кеша.

3.3. Передача данных из хуков в шаблоны

В обработчике хука можно напрямую манипулировать переменными, которые затем попадут в шаблон. Например, в хуке header.tags можно установить Cot::$out['subtitle'].

Для добавления тегов в конкретный шаблон обычно используется объект $t (доступен в хуках, связанных с шаблонами):

$t->assign('MY_CUSTOM_TAG', 'Значение');

Часть IV. Расширение собственных модулей с помощью хуков

Если вы разрабатываете модуль или плагин, вы можете предоставить точки расширения для других разработчиков, следуя приведённому выше шаблону вызова cot_getextplugins(). Рекомендуется создавать хуки для значимых событий: create, update, delete, display и т.д. Имена хуков должны следовать соглашению расширение.часть.событие.состояние.

Пример создания хука mymodule.order.create.done:

/* === Hook === */
foreach (cot_getextplugins('mymodule.order.create.done') as $pl) {
    include $pl;
}
/* ============= */

Разработчик плагина может зарегистрировать обработчик:

/* ====================
[BEGIN_COT_EXT]
Hooks=mymodule.order.create.done
Order=10
[END_COT_EXT]
==================== */

// Здесь доступны все переменные, существующие в точке вызова

Часть V. Практический пример: список страниц с фильтром и пагинацией через контроллер и хуки

Ниже демонстрируется, как можно использовать XTemplate, контроллер и хук pagetags.main для вывода списка страниц с дополнительными данными.

Контроллер PageController (файл modules/pages/controllers/PageController.php):

namespace cot\modules\pages\controllers;

use cot\controllers\BaseController;

class PageController extends BaseController
{
    public function actionList(): string
    {
        $cat = cot_import('cat', 'G', 'ALP');
        $page = cot_import('d', 'G', 'INT', 1) - 1;
        $perPage = 10;

        $total = PageRepository::countByCat($cat);
        $items = PageRepository::findByCat($cat, $page * $perPage, $perPage);

        $pagenav = cot_pagenav('pages', ['cat' => $cat], $page, $total, $perPage);
        $t = new XTemplate(cot_tplfile('pages.list', 'module'));
        $t->assign(cot_generatePaginationTags($pagenav));

        foreach ($items as $item) {
            $t->assign([
                'PAGE_ROW_ID'    => $item['page_id'],
                'PAGE_ROW_TITLE' => htmlspecialchars($item['page_title']),
                'PAGE_ROW_URL'   => cot_url('page', ['id' => $item['page_id']])
            ]);
            $t->parse('MAIN.PAGE_ROW');
        }

        $t->parse('MAIN');
        return $t->text('MAIN');
    }
}

Плагин может добавить дополнительный тег, например цену, через хук pagetags.main:

<?php
/* ====================
[BEGIN_COT_EXT]
Hooks=pagetags.main
Order=10
[END_COT_EXT]
==================== */

defined('COT_CODE') or die('Wrong URL');

// $item_data — массив данных текущей страницы, $temp_array — ссылка на массив тегов
if (!empty($item_data['page_extra_price'])) {
    $temp_array['PAGE_ROW_PRICE'] = number_format($item_data['page_extra_price'], 2);
}

Шаблон pages.list.tpl:

<!-- BEGIN: MAIN -->
<h1>{PAGE_TITLE}</h1>

<div class="row">
  <!-- BEGIN: PAGE_ROW -->
  <div class="col-12 mb-3">
    <h5><a href="{PAGE_ROW_URL}">{PAGE_ROW_TITLE}</a></h5>
    <!-- IF {PAGE_ROW_PRICE} -->
    <p>Цена: {PAGE_ROW_PRICE} руб.</p>
    <!-- ENDIF -->
  </div>
  <!-- END: PAGE_ROW -->
</div>

<div class="pagination">
  {PAGENAV_PREV}{PAGENAV_PAGES}{PAGENAV_NEXT}
</div>
<!-- END: MAIN -->

Заключение

XTemplate и система хуков являются краеугольными камнями расширяемости Cotonti. XTemplate отделяет логику от представления, хуки позволяют безопасно модифицировать поведение системы. Знание этих инструментов необходимо каждому разработчику расширений для Cotonti. Приведённые примеры основаны на реальных возможностях ядра (включая Verona 1.0.x).



Отзывы

Отзывов пока нет


Комментарии (0)

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям

Похожие страницы

Подробное руководство: создание адаптивного сайдбара для COTONTI CMF
1 Подробное руководство: создание адаптивного сайдбара для COTONTI CMF Версия для публичной статьи – без полных
Google обновляет руководство по SEO для начинающих
2 Google обновляет руководство по SEO для начинающих, чтобы сосредоточиться на целевой аудитории Руководство Google по
Установка Open Server Panel 6.5.1: Руководство для новичков 2026
3 СодержаниеУстановка Open Server Panel 6.5.+. Подробное руководство для новичков – Первый запуск на PHP 8.5Что такое
Руководство по маркетингу роста 2024. Часть 1.
4 Сосредоточьтесь на пожизненной ценности клиента Используйте это руководство для настройки тестов маркетинга роста,
Руководство по маркетингу роста 2024. Часть 2 (вторая)
5 Вопрос: Как мне привлечь самых идеальных клиентов? Узнайте, где общаются ваши идеальные клиенты и какие типы контента

Автор контента

webitproff

Оффлайн

Carbonate Sodium

Последняя авторизация: 11.07.2026 19:44

  • Чем могу помочь?

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

  • Разработки на GitHub бесплатно
    • Страница размещена: 29.04.2026 17:28
    • Последнее обновление: 29.04.2026 18:03
    • Язык: