Файловые элементы pdoTools: работа с чанками, сниппетами и шаблонами из файлов

MODX файловые элементы Дополнения

Файловые элементы pdoTools — это возможность загружать чанки, сниппеты и шаблоны напрямую из файлов на диске вместо хранения в базе данных MODX. Это упрощает командную разработку через Git, ускоряет работу с кодом в IDE и позволяет использовать современные инструменты разработки. В этом уроке разберём как настроить, использовать и оптимизировать файловые элементы в связке с шаблонизатором Fenom.

Содержание
  1. Что такое файловые элементы
  2. Преимущества файловых элементов
  3. Версионный контроль через Git
  4. Работа в профессиональной IDE
  5. Производительность
  6. Модульность и переиспользование
  7. Интеграция с CI/CD
  8. Наследование шаблонов через Fenom
  9. Настройка pdoTools для работы с файлами
  10. Системные настройки
  11. Поддерживаемые расширения файлов
  12. Базовый синтаксис: биндинг @FILE
  13. Загрузка через PHP API
  14. Кастомный путь к файлам
  15. Вызов в классических тегах MODX
  16. Работа с Fenom: include, extends, наследование
  17. Include — вставка фрагментов
  18. Extends — наследование шаблонов
  19. Источники шаблонов в Fenom
  20. Вызов через модификаторы
  21. Структура проекта и организация файлов
  22. Рекомендуемая структура
  23. Принципы организации
  24. Кэширование файловых элементов
  25. Поведение по умолчанию
  26. Включение кэша Fenom
  27. Настройка опций Fenom
  28. Нативный файловый кэш Fenom
  29. Практические примеры для реальных проектов
  30. Пример 1. Шаблон страницы с наследованием
  31. Пример 2. Модульные чанки с параметрами
  32. Пример 3. Файловый сниппет для API
  33. Пример 4. Плагины с файловой логикой
  34. Безопасность и подводные камни
  35. Защита файлов от прямого доступа
  36. Валидация путей
  37. Права доступа к файлам
  38. Типичные ошибки
  39. Заключение и лучшие практики
  40. Лучшие практики

Что такое файловые элементы

Файловые элементы — это чанки, сниппеты и шаблоны, которые хранятся в файловой системе сервера вместо таблиц базы данных. pdoTools загружает их динамически через специальный биндинг @FILE.

Классический подход MODX:

  • Чанки → таблица modx_site_htmlsnippets
  • Сниппеты → таблица modx_site_snippets
  • Шаблоны → таблица modx_site_templates

Подход с файловыми элементами:

  • Чанки → файлы .tpl или .html
  • Сниппеты → файлы .php
  • Шаблоны → файлы .tpl с поддержкой Fenom

Все элементы загружаются из директории, указанной в системной настройке pdotools_elements_path (по умолчанию {core_path}components/pdotools/elements/).

Преимущества файловых элементов

Версионный контроль через Git

Храните весь код в репозитории: делайте коммиты, ветки, pull-request’ы без экспорта/импорта элементов из БД. Команда видит все изменения в diff’ах.

Пример workflow:

git diff chunks/header.tpl
# Видны все изменения в чанке
git commit -m "Добавил мобильное меню в header"
git push origin feature/mobile-menu

Работа в профессиональной IDE

Полная поддержка PhpStorm, VS Code, Sublime Text: автодополнение, подсветка синтаксиса, refactoring, встроенная отладка. В админке MODX этого нет.

Производительность

Загрузка файла с диска через opcache быстрее, чем SELECT из БД, особенно для больших элементов. Меньше нагрузка на MySQL.

Модульность и переиспользование

Разбивайте код на логические модули:

chunks/
  ├── common/header.tpl    # Общая шапка
  ├── common/footer.tpl    # Общий футер
  ├── blog/post.tpl        # Карточка поста
  └── shop/product.tpl     # Карточка товара

Подключайте через {include} без дублирования кода.

Интеграция с CI/CD

Автоматизируйте деплоймент через GitLab CI, GitHub Actions, Jenkins — файлы автоматически попадают на сервер без ручного копирования в админку.

Наследование шаблонов через Fenom

Используйте {extends} для создания базовых layout’ов — это невозможно для элементов из БД без хаков.

Настройка pdoTools для работы с файлами

Системные настройки

Путь: Система → Настройки → pdoTools

Настройка Значение Описание
pdotools_elements_path {core_path}elements/ Базовая директория для файловых элементов
pdotools_fenom_parser Да Обязательно для работы {include 'file:...'}
pdotools_fenom_cache Нет (dev) / Да (prod) Кэширование скомпилированных Fenom-шаблонов
pdotools_fenom_options {"auto_reload":true} JSON-опции Fenom (см. раздел Кэширование)

Рекомендация: создайте свою папку вне core/components/pdotools/:

# В настройках укажите
pdotools_elements_path = {base_path}elements/

# Структура
/elements/
  ├── chunks/
  ├── snippets/
  └── templates/

Это защитит файлы от случайного удаления при обновлении pdoTools.

Поддерживаемые расширения файлов

  • Чанки: .tpl, .html
  • Сниппеты: .php
  • Другие расширения игнорируются для безопасности

Базовый синтаксис: биндинг @FILE

Загрузка через PHP API

Чанк:

if ($pdoTools = $modx->getService('pdoTools')) {
    $chunk = $pdoTools->getChunk('@FILE chunks/my_chunk.tpl', [
        'placeholder' => 'value'
    ]);
    echo $chunk;
}

Сниппет:

if ($pdoTools = $modx->getService('pdoTools')) {
    $result = $pdoTools->runSnippet('@FILE snippets/my_snippet.php', [
        'param' => 'value'
    ]);
    return $result;
}

Кастомный путь к файлам

Если файлы находятся вне pdotools_elements_path, укажите elementsPath:

$chunk = $pdoTools->getChunk('@FILE custom/my_chunk.tpl', [
    'placeholder' => 'value',
    'elementsPath' => MODX_ASSETS_PATH . 'mytheme/'
]);

Теперь файл будет загружен из assets/mytheme/custom/my_chunk.tpl.

Вызов в классических тегах MODX

В контенте ресурса или шаблоне:

[[$@FILE chunks/header.tpl]]
[[!@FILE snippets/latest_news.php? &limit=`5`]]

Префикс ! делает вызов некешированным.

Работа с Fenom: include, extends, наследование

Fenom добавляет мощные инструменты шаблонизации, недоступные в классических тегах MODX.

Include — вставка фрагментов

Синтаксис: {include 'file:path/to/file.tpl'}

Пример в шаблоне:

<!DOCTYPE html>
<html>
<head>
    {include 'file:chunks/common/head.tpl'}
</head>
<body>
    {include 'file:chunks/common/header.tpl'}
    
    <main>{$content}</main>
    
    {include 'file:chunks/common/footer.tpl'}
</body>
</html>

Передача параметров:

{include 'file:chunks/button.tpl' 
    text='Купить' 
    url='/shop' 
    class='btn-primary'
}

В chunks/button.tpl:

<a href="{$url}" class="btn {$class}">
    {$text}
</a>

Extends — наследование шаблонов

Создайте базовый layout и переопределяйте блоки в дочерних шаблонах.

Базовый шаблон (templates/base.tpl):

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>{block 'title'}{$_modx->config.site_name}{/block}</title>
    {block 'head'}{/block}
</head>
<body>
    {include 'file:chunks/common/header.tpl'}
    
    <main class="container">
        {block 'content'}
            <p>Контент по умолчанию</p>
        {/block}
    </main>
    
    {include 'file:chunks/common/footer.tpl'}
    {block 'scripts'}{/block}
</body>
</html>

Дочерний шаблон (templates/blog_post.tpl):

{extends 'file:templates/base.tpl'}

{block 'title'}
    {$pagetitle} | {$_modx->config.site_name}
{/block}

{block 'head'}
    <meta name="description" content="{$description}">
    <link rel="stylesheet" href="/assets/css/blog.css">
{/block}

{block 'content'}
    <article class="post">
        <h1>{$pagetitle}</h1>
        <time>{$publishedon | date:'%d.%m.%Y'}</time>
        <div class="post-content">
            {$content}
        </div>
        {if $tags}
            {include 'file:chunks/blog/tags.tpl' tags=$tags}
        {/if}
    </article>
{/block}

{block 'scripts'}
    <script src="/assets/js/comments.js"></script>
{/block}

Использование в MODX:

В шаблоне MODX (в БД) укажите всего одну строку:

{include 'file:templates/blog_post.tpl'}

Весь layout загрузится из файлов с наследованием от base.tpl.

Источники шаблонов в Fenom

Файловые: {include 'file:path.tpl'}

Из БД (чанк): {include 'myChunk'} или {include '10'} (по ID)

Из шаблона MODX: {include 'template:MyTemplate'} или {include 'template:1'}

Вызов через модификаторы

Чанк:

{'@FILE chunks/my_chunk.tpl' | chunk : ['name' => 'John']}

Сниппет:

{'@FILE snippets/fetch_data.php' | snippet : ['limit' => 10]}

С кастомным путём:

{$_modx->getChunk('@FILE custom/chunk.tpl', [ 'param' => 'value', 'elementsPath' => '/path/to/files/' ])}

Структура проекта и организация файлов

Рекомендуемая структура

elements/
├── chunks/                      # Чанки (UI-фрагменты)
│   ├── common/                  # Общие элементы
│   │   ├── head.tpl
│   │   ├── header.tpl
│   │   ├── footer.tpl
│   │   └── sidebar.tpl
│   ├── blog/                    # Блог
│   │   ├── post_card.tpl        # Карточка поста
│   │   ├── post_full.tpl        # Полный пост
│   │   └── comments.tpl
│   ├── shop/                    # Магазин
│   │   ├── product_card.tpl
│   │   ├── product_full.tpl
│   │   └── cart.tpl
│   └── partials/                # Переиспользуемые компоненты
│       ├── button.tpl
│       ├── modal.tpl
│       └── form_field.tpl
├── snippets/                    # Сниппеты (логика)
│   ├── utils/                   # Утилиты
│   │   ├── date_formatter.php
│   │   ├── image_resizer.php
│   │   └── cache_helper.php
│   ├── api/                     # API-интеграции
│   │   ├── fetch_news.php
│   │   └── send_email.php
│   └── custom/                  # Кастомная логика
│       └── calculate_price.php
├── templates/                   # Шаблоны страниц
│   ├── base.tpl                 # Базовый layout
│   ├── home.tpl                 # Главная
│   ├── page.tpl                 # Обычная страница
│   ├── blog_list.tpl            # Список постов
│   └── blog_post.tpl            # Отдельный пост
└── plugins/                     # Плагины (если нужны файловые)
    └── custom_events.php

Принципы организации

  1. Группировка по функциональности: chunks/blog/, chunks/shop/
  2. Переиспользуемость: partials/ для универсальных компонентов
  3. Разделение логики и представления: сниппеты отдельно от чанков
  4. Говорящие имена: post_card.tpl вместо pc.tpl

Кэширование файловых элементов

Поведение по умолчанию

  • Файловые сниппеты не кэшируются — выполняются каждый раз
  • Файловые чанки через @FILE кэшируются при включении pdotools_fenom_cache

Включение кэша Fenom

Системная настройка:

pdotools_fenom_cache = Yes

Скомпилированные шаблоны сохраняются в core/cache/default/fenom/:

cache/default/fenom/
├── file/                   # Файловые элементы
│   └── 5f4dcc3b5aa765d61d8327deb882cf99.php
├── chunk/                  # Чанки из БД
│   └── 90.cache.php
└── inline/                 # @INLINE чанки
    └── 35e115c27fdc3814.php

Настройка опций Fenom

Путь: pdotools_fenom_options (JSON)

Development (разработка):

{
  "force_compile": true,
  "disable_cache": true,
  "auto_reload": true,
  "force_include": true
}
  • force_compile — рекомпиляция при каждом запросе (видите изменения сразу)
  • auto_reload — автообновление при изменении файла
  • disable_cache — отключение кэша (для отладки)

Production (продакшн):

{
  "force_compile": false,
  "disable_cache": false,
  "auto_reload": false,
  "force_include": true
}

Максимальная скорость — шаблоны компилируются один раз и кэшируются.

Нативный файловый кэш Fenom

Для работы только с файлами (без @FILE) используйте {include 'file:...'} — Fenom сам управляет кэшем эффективнее:

{* Рекомендуемый способ *}
{include 'file:chunks/header.tpl'}

{* Вместо *}
{$_modx->getChunk('@FILE chunks/header.tpl')}

Практические примеры для реальных проектов

Пример 1. Шаблон страницы с наследованием

В MODX создайте шаблон «Blog Post» (в БД):

{include 'file:templates/blog_post.tpl'}

Файл templates/base.tpl (базовый layout):

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{block 'title'}{$_modx->config.site_name}{/block}</title>
    {block 'meta'}{/block}
    <link rel="stylesheet" href="/assets/css/main.css">
    {block 'styles'}{/block}
</head>
<body>
    {include 'file:chunks/common/header.tpl'}
    
    {block 'content'}
        <div class="container">
            <h1>Контент по умолчанию</h1>
        </div>
    {/block}
    
    {include 'file:chunks/common/footer.tpl'}
    
    <script src="/assets/js/main.js"></script>
    {block 'scripts'}{/block}
</body>
</html>

Файл templates/blog_post.tpl:

{extends 'file:templates/base.tpl'}

{block 'title'}
    {$pagetitle} | Блог | {$_modx->config.site_name}
{/block}

{block 'meta'}
    <meta name="description" content="{$description ?: $introtext}">
    <meta property="og:title" content="{$pagetitle}">
    <meta property="og:image" content="{$image}">
{/block}

{block 'content'}
    <article class="blog-post container">
        <h1>{$pagetitle}</h1>
        <div class="post-meta">
            <time datetime="{$publishedon}">{$publishedon | date:'%d.%m.%Y'}</time>
            <span class="author">Автор: {$createdby | userinfo:'fullname'}</span>
        </div>
        
        <div class="post-content">
            {$content}
        </div>
        
        {if $tags}
            <div class="tags">
                {foreach $tags as $tag}
                    <span class="tag">{$tag}</span>
                {/foreach}
            </div>
        {/if}
        
        {include 'file:chunks/blog/related_posts.tpl' id=$id}
    </article>
{/block}

{block 'scripts'}
    <script src="/assets/js/comments.js"></script>
{/block}

Пример 2. Модульные чанки с параметрами

Файл chunks/partials/button.tpl:

<a href="{$url}" 
   class="btn {$class ?: 'btn-primary'} {$size ?: ''}" 
   {if $target}target="{$target}"{/if}
   {if $disabled}disabled{/if}>
    {if $icon}
        <i class="icon-{$icon}"></i>
    {/if}
    {$text}
</a>

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

{include 'file:chunks/partials/button.tpl' 
    text='Купить сейчас' 
    url='/cart/add' 
    class='btn-success' 
    icon='cart'
}

{include 'file:chunks/partials/button.tpl' 
    text='Подробнее' 
    url=$link 
    class='btn-outline'
}

Пример 3. Файловый сниппет для API

Файл snippets/api/fetch_news.php:

<?php
/**
 * Загружает новости из внешнего API
 * 
 * @param int $limit Количество новостей
 * @param string $category Категория
 * @return string JSON
 */

$limit = (int)($limit ?? 5);
$category = $category ?? 'all';

// Ваша логика
$news = fetchFromAPI($category, $limit);

return json_encode($news);

Вызов в Fenom:

{var $news_json = '@FILE snippets/api/fetch_news.php' | snippet : ['limit' => 10, 'category' => 'tech']}
{var $news = $news_json | json_decode : true}

{foreach $news as $item}
    <div class="news-item">
        <h3>{$item.title}</h3>
        <p>{$item.description}</p>
    </div>
{/foreach}

Пример 4. Плагины с файловой логикой

Создайте плагин в MODX (в БД) с событием OnLoadWebDocument:

<?php
if ($pdoTools = $modx->getService('pdoTools')) {
    $pdoTools->runSnippet('@FILE plugins/seo_redirect.php', $scriptProperties);
}

Файл plugins/seo_redirect.php:

<?php
/**
 * SEO редиректы с www на без www
 */

switch ($modx->event->name) {
    case 'OnLoadWebDocument':
        $host = $_SERVER['HTTP_HOST'];
        if (strpos($host, 'www.') === 0) {
            $new_host = str_replace('www.', '', $host);
            $url = 'https://' . $new_host . $_SERVER['REQUEST_URI'];
            $modx->sendRedirect($url, ['responseCode' => 'HTTP/1.1 301 Moved Permanently']);
        }
        break;
}

Безопасность и подводные камни

Защита файлов от прямого доступа

Проблема: файлы .tpl и .php доступны по URL вроде site.com/elements/chunks/header.tpl.

Решение 1: храните файлы вне web-root (в core/elements/ вместо assets/elements/).

Решение 2: создайте .htaccess в папке elements/:

<Files ~ "\.(tpl|html|php)$">
    Require all denied
</Files>

Для Nginx:

location ~* \.(tpl|html|php)$ {
    deny all;
}

Валидация путей

Опасно:

// $_GET['file'] может быть '../../../etc/passwd'
$chunk = $pdoTools->getChunk('@FILE ' . $_GET['file']);

Безопасно:

$allowed_chunks = ['header', 'footer', 'sidebar'];
$file = in_array($_GET['chunk'], $allowed_chunks) 
    ? $_GET['chunk'] 
    : 'default';
$chunk = $pdoTools->getChunk('@FILE chunks/' . $file . '.tpl');

Права доступа к файлам

Установите корректные права:

chmod 755 elements/
chmod 644 elements/chunks/*.tpl
chmod 755 elements/snippets/*.php

Владелец — пользователь веб-сервера (обычно www-data или apache).

Типичные ошибки

1. Файл не найден:

Error loading chunk '@FILE chunks/missing.tpl'

Проверьте путь относительно pdotools_elements_path и существование файла.

2. Синтаксическая ошибка в Fenom:

Fenom\CompileException: Unexpected token...

Включите &showLog=1 в вызове сниппета или проверьте логи MODX в core/cache/logs/.

3. Кэш не обновляется:
Если force_compile = false, очистите кэш MODX или удалите core/cache/default/fenom/.

4. Конфликт с путями:

// Неправильно — ищет в pdotools_elements_path + /absolute/path
$pdoTools->getChunk('@FILE /absolute/path/chunk.tpl');

// Правильно
$pdoTools->getChunk('@FILE chunk.tpl', [
    'elementsPath' => '/absolute/path/'
]);

Заключение и лучшие практики

Файловые элементы pdoTools — это современный подход к разработке на MODX. Вы научились:

  • Настраивать pdoTools для работы с файлами через pdotools_elements_path.
  • Использовать биндинг @FILE для загрузки чанков и сниппетов.
  • Применять Fenom с {include} и {extends} для модульных шаблонов.
  • Организовывать структуру проекта логично и масштабируемо.
  • Оптимизировать через кэширование Fenom.
  • Обеспечивать безопасность через права доступа и валидацию.

Лучшие практики

  1. Используйте {include 'file:...'} вместо @FILE — быстрее благодаря нативному кэшу Fenom.
  2. Создавайте базовые layout’ы через {extends} — избегайте дублирования.
  3. Группируйте файлы по функциональностиchunks/blog/, chunks/shop/.
  4. Включайте force_compile только на dev — на продакшне отключайте.
  5. Защищайте файлы через .htaccess — запретите прямой доступ к .tpl и .php.
  6. Используйте Git — коммитьте изменения, создавайте ветки, делайте code review.
  7. Документируйте сниппеты — добавляйте PHPDoc с описанием параметров.

Файловые элементы в pdoTools — это шаг к современному веб-разработке в MODX. Они сочетают гибкость файловой системы с мощью Fenom, делая проекты масштабируемыми и удобными. Для глубокого погружения изучите документацию pdoTools и экспериментируйте с примерами.

Оцените статью
MODX 3
Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.