Кастомні снипети MODX: швидкі PHP-рішення з pdoTools

Стандартні снипети MODX часто працюють повільно через зайві запити до бази, що гальмує сайт і дратує користувачів. Ми розробляємо кастомні PHP-сніпети з використанням pdoTools, оптимізуючи кожен запит і впроваджуючи кешування. Наша команда реалізує рішення під ключ — від аудиту до підтримки, забезпечуючи стабільну роботу навіть при високих навантаженнях.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Кастомні снипети MODX: швидкі PHP-рішення з pdoTools
Середній
~2-3 дні
Часті запитання

Типовий снипет — це копіпаста без оптимізації

Уявіть: ваш каталог товарів на MODX завантажується 5 секунд, а кожен клік по категорії — вічність. Причина — неоптимізовані снипети, які роблять десятки запитів до бази. Типова ситуація: розробник скопіював перший-ліпший код, не подумавши про кешування. Результат — N+1 запитів, падіння продуктивності та негативний досвід користувача. Як це виправити? Використовувати правильні патерни.

Розробка снипета MODX часто починається з копіювання першого прикладу з документації. Результат — N+1 запитів, відсутність кешування та вразливості, які перетворюють просте завдання на гальмо для всього сайту. Ми використовуємо перевірені архітектурні патерни: єдиний запит, кешування та строгу типізацію параметрів. Понад 5 років досвіду та більше 50 проєктів на MODX дозволяють нам створювати снипети, які витримують тисячі запитів без втрати продуктивності.

Як уникнути N+1 запитів у MODX?

Проблема N+1 виникає, коли ви отримуєте колекцію ресурсів, а потім для кожного підвантажуєте TV-значення через getTVValue у циклі. При 100 ресурсах це 101 SQL-запит. Рішення — використовувати JOIN або pdoFetch. Нативний MODX API дозволяє написати JOIN вручну, але pdoTools робить це автоматично і швидше. Докладніше про проблему читайте у статті N+1 query problem.

Чому важливо кешувати снипети?

Кешування зменшує навантаження на базу даних і прискорює сторінку. Без кешу кожен виклик снипета — це SQL-запит. На проєкті з каталогом 5000 товарів вихідний снипет виконувався 4 секунди. Після додавання кешування час упав до 0.2 секунди для наступних запитів. Кеш потрібно інвалідувати при зміні даних або встановлювати час життя, наприклад, 30 хвилин.

Структура снипета з безпечною вибіркою

Нативний запит з JOIN

<?php
// Снипет ProductList
// Виклик: [[!ProductList? &category=`5` &limit=`12` &sort=`price`]]

// Отримати параметри (з default значеннями)
$categoryId = (int)($scriptProperties['category'] ?? 0);
$limit = (int)($scriptProperties['limit'] ?? 10);
$offset = (int)($scriptProperties['offset'] ?? 0);
$sortField = $scriptProperties['sort'] ?? 'menuindex';
$sortDir = $scriptProperties['sortdir'] ?? 'ASC';
$tpl = $scriptProperties['tpl'] ?? 'productCard';

// Запит до ресурсів через MODX API
$c = $modx->newQuery('modResource');
$c->where([
    'parent' => $categoryId,
    'published' => 1,
    'deleted' => 0,
    'class_key' => 'modDocument',
]);

// Отримати TV значення через Join
$c->innerJoin('modTemplateVarResource', 'TVPrice', [
    'TVPrice.tmplvarid' => $modx->getObject('modTemplateVar', ['name' => 'price'])->id,
    'TVPrice.contentid = modResource.id',
]);
$c->select('modResource.*, TVPrice.value AS price');
$c->sortby($sortField, $sortDir);
$c->limit($limit, $offset);
$resources = $modx->getCollection('modResource', $c);

if (empty($resources)) return '';

$output = '';
foreach ($resources as $resource) {
    $data = array_merge($resource->toArray(), [
        'price' => $resource->get('price'),
        'link' => $modx->makeUrl($resource->id, '', '', 'full'),
        'image' => $resource->getTVValue('product_image'),
    ]);

    // Чанк для виведення картки
    $output .= $modx->getChunk($tpl, $data);
}

return $output;

Снипет з pdoTools

<?php
// Снипет ProductSearch з pdoTools
if (!$modx->loadClass('pdoFetch', MODX_CORE_PATH . 'components/pdotools/model/pdotools/', false, true)) {
    return 'pdoTools не встановлено';
}
$pdoFetch = new pdoFetch($modx, $scriptProperties);
$pdoFetch->addWhere([
    'modResource.parent' => (int)($scriptProperties['category'] ?? 0),
    'modResource.published' => 1,
]);
// TV join
$pdoFetch->addTVs('price,product_image,short_description');
$result = $pdoFetch->run();
return $result;

Кешування результатів

<?php
// Кешувати результат на 30 хвилин
$cacheKey = 'products_' . md5(json_encode($scriptProperties));
$cacheOptions = [
    xPDO::OPT_CACHE_KEY => 'default',
    xPDO::OPT_CACHE_EXPIRES => 1800
];
$cached = $modx->cacheManager->get($cacheKey, $cacheOptions);
if ($cached !== null) return $cached;
// ... запит ...
$output = generateOutput($resources);
$modx->cacheManager->set($cacheKey, $output, 1800, $cacheOptions);
return $output;

Інтеграція із зовнішнім API

<?php // Снипет WeatherWidget — погода з OpenWeatherMap
$city = $scriptProperties['city'] ?? 'Moscow';
$apiKey = $modx->getOption('weather_api_key');
$tpl = $scriptProperties['tpl'] ?? 'weatherWidget';
$cacheKey = 'weather_' . $city;
$cached = $modx->cacheManager->get($cacheKey, [xPDO::OPT_CACHE_EXPIRES => 1800]);
if ($cached !== null) {
    return $modx->getChunk($tpl, $cached);
}
$url = "https://api.openweathermap.org/data/2.5/weather?q={$city}&appid={$apiKey}&units=metric&lang=ua";
$response = file_get_contents($url);
if (!$response) return '';
$data = json_decode($response, true);
if (!$data || $data['cod'] !== 200) return '';
$weather = [
    'city' => $data['name'],
    'temp' => round($data['main']['temp']),
    'feels_like' => round($data['main']['feels_like']),
    'description' => $data['weather'][0]['description'],
    'icon' => "https://openweathermap.org/img/wn/{$data['weather'][0]['icon']}@2x.png",
    'humidity' => $data['main']['humidity'],
];
$modx->cacheManager->set($cacheKey, $weather, 1800);
return $modx->getChunk($tpl, $weather);

Які параметри передавати в снипет?

[[!ProductList? &category=`[[*id]]` &limit=`12` &tpl=`productCardTpl` &sort=`price` &sortdir=`ASC` ]] 

! перед ім'ям — некешований виклик (динамічний контент). Без ! — кешований (статичний блок, однаковий для всіх).

Таблиця порівняння підходів

Аспект Нативний MODX pdoTools З кешуванням
Кількість SQL-запитів N+1 (колекція + TV) 1 (один JOIN) 1 раз, потім із кешу
Простота написання Потрібен ручний JOIN Автоматичний підгруз TV Додатково 3 рядки
Продуктивність на 1000 ресурсів ~2–3 с ~0.3–0.5 с ~0.01 с після першого запиту
Підтримка фільтрів Ручне додавання where Вбудовані addWhere Не впливає

Таблиця типових параметрів снипета

Параметр Тип Опис Default
category int ID батьківського ресурсу 0
limit int Кількість записів для виведення 10
sort string Поле сортування menuindex
sortdir string Напрямок сортування (ASC/DESC) ASC
tpl string Ім'я чанка для виведення productCard

Типові помилки при розробці снипетів

  • N+1 запити: отримання TV через getTVValue у циклі. Рішення — JOIN або pdoFetch.
  • Відсутність фільтрації вхідних даних: параметри без приведення типу (категорія — рядок, а не int).
  • Ігнорування кешування: кожен виклик снипета завантажує БД, навіть якщо дані не змінилися.
  • Жорстко зашиті ID ресурсів: використовуйте плейсхолдери та параметри.
Чек-лист: як не допустити помилок
  • [ ] Використовувати prepared statements
  • [ ] Приводити всі вхідні параметри до потрібного типу
  • [ ] Додати кешування з унікальним ключем
  • [ ] Використовувати pdoFetch для складних вибірок
  • [ ] Документувати параметри та чанки

Що входить до роботи

  • Вихідний код снипета з коментарями.
  • Документація: опис параметрів, приклади виклику, опис логіки.
  • Налаштування кешування: підбір часу життя та ключа.
  • Інструкція зі встановлення: додавання снипета, створення чанків, тестування.
  • Гарантія підтримки: якщо виникнуть питання, ми допоможемо протягом місяця після здачі.

Процес розробки снипета під ключ

  1. Аналітика: вивчаємо ТЗ, які дані виводити, звідки брати, умови фільтрації.
  2. Проектування: визначаємо структуру запиту, обираємо метод (нативний або pdoTools), продумуємо кешування.
  3. Реалізація: пишемо PHP-код снипета та чанки шаблонів.
  4. Тестування: перевіряємо на крайніх значеннях (порожні категорії, 0 записів), вимірюємо час виконання.
  5. Деплой: розміщуємо на бойовому сервері, налаштовуємо кеш, даємо документацію з виклику.

Терміни та вартість

Терміни: від 0,5 дня для простого снипета до 5 днів для складного з інтеграцією API. Точну оцінку даємо після знайомства з проєктом. Вартість розраховується індивідуально.

Зв'яжіться з нами для обговорення вашого проєкту. Замовте розробку снипета прямо зараз — отримайте швидкий і безпечний код. Досвід розробки під MODX — понад 5 років. Пишіть, і ми оцінимо завдання та запропонуємо оптимальне рішення.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060