xPDO в MODX: чек-лист запросов без лишней нагрузки
Запрос к xPDO может вернуть правильные данные и всё равно замедлить страницу, загрузить лишние поля или сломаться после обновления MODX. Чек-лист помогает проверить выборку, условия, связи, пагинацию и поведение под нагрузкой.

xPDO-запрос в MODX часто выглядит безобидно: получить несколько ресурсов, отсортировать их и вывести в чанке. Но выборка без ограничения, вызов getObject в цикле или неявная загрузка связанных объектов быстро превращают страницу в источник лишних SQL-запросов. Этот чек-лист рассчитан на MODX Revolution 2.8.x и 3.x; примеры используют общий менеджерский API, но перед переносом кода проверьте версию xPDO в конкретном проекте.
Что проверить до написания запроса
- Версия MODX и xPDO. В MODX Revolution 2.x обычно используется ветка xPDO 2.x, в MODX 3 — xPDO 3.x. Дополнительные классы и расширения могут иметь собственные ограничения.
- Класс объекта. Для ресурса это обычно
modResource, для пользователя —modUser. Не подставляйте имя таблицы вместо имени xPDO-класса. - Нужен объект или массив данных. Объект удобен, когда вы вызываете методы и связанные объекты. Для списка карточек часто достаточно ограниченной выборки полей.
- Нужны ли скрытые ресурсы. Условия
published,deleted,hidemenuи доступ по контексту не добавляются автоматически во все пользовательские запросы. - Какой объём результата допустим. Даже рабочий запрос должен иметь
limit, пагинацию или другое ограничение, если число строк может расти.
Объект xPDO1 — это PHP-представление записи или связанной модели, через которое MODX читает и изменяет данные.
Главный вопрос при ревью xPDO — не «вернёт ли запрос данные», а «сколько строк, полей и дополнительных запросов он создаст на реальном сайте».
Чек-лист построения выборки
1. Выберите класс, а не таблицу
Запрос строится вокруг xPDO-класса. Такой подход сохраняет связь с картой модели MODX и не привязывает код к имени таблицы, которое может включать префикс.
<?php
$query = $modx->newQuery('modResource');
$query->where([
'published' => 1,
'deleted' => 0,
]);
$query->sortby('publishedon', 'DESC');
$query->limit(20);
$resources = $modx->getCollection('modResource', $query);
Проверка: запрос возвращает коллекцию объектов modResource, а в SQL нет ручного имени таблицы с префиксом. Для MODX 3.x проверьте, что используемый класс зарегистрирован в карте пакета проекта.
2. Запишите условия явно
Не рассчитывайте, что getCollection сама отфильтрует неопубликованные или удалённые ресурсы. Для публичной выборки обычно нужны как минимум признаки публикации и удаления, а также ограничение по контексту, если сайт мультиязычный.
<?php
$query = $modx->newQuery('modResource');
$query->where([
'published' => 1,
'deleted' => 0,
'context_key' => $modx->context->get('key'),
]);
$query->select('id,parent,pagetitle,uri,publishedon');
$query->sortby('publishedon', 'DESC');
$query->limit(20);
$resources = $modx->getCollection('modResource', $query);
Проверка: ресурс из другого контекста или с флагом deleted = 1 не попадает в результат. Логику доступа по группам пользователей этот фильтр не заменяет.
3. Ограничьте поля
Если карточке нужны только идентификатор, заголовок и URI, не загружайте все поля ресурса. Это особенно важно при больших текстовых полях и выборках из каталога.
Уменьшение списка в select не превращает объект в обычный массив. Если код ниже по цепочке ожидает поле, которого нет в выборке, он может получить пустое значение. Список полей должен соответствовать реальному шаблону вывода.
4. Разделите один объект и коллекцию
| Задача | Подход | Цена и риск |
|---|---|---|
| Найти одну запись по уникальному условию | getObject | Простой код, но нужно обработать результат false или null в зависимости от версии и способа вызова. |
| Получить список объектов | newQuery + getCollection | Удобно для методов объектов, но каждый объект потребляет память. |
| Получить только значения для вывода | Ограниченный select в коллекции | Меньше данных, но нельзя обращаться к невыбранным полям без изменения запроса. |
| Посчитать строки | Отдельный запрос подсчёта | Пагинация получает дополнительный SQL-запрос; не смешивайте подсчёт и вывод без необходимости. |
Критерий выбора простой: если дальше вызываются методы модели или связи, оставляйте объекты; если вы строите простой список и знаете поля, ограничивайте выборку.
5. Проверьте сортировку и пагинацию
Сортировка без явного поля может давать разный порядок при одинаковых значениях. Для стабильной пагинации добавьте второй критерий, например идентификатор ресурса. Не передавайте имя поля и направление сортировки напрямую из запроса пользователя без проверки допустимого списка.
limit без смещения показывает только первую порцию. Для следующей страницы нужен согласованный расчёт offset и одинаковая сортировка. Инвалидация кеша списка — отдельная тема; здесь важно не смешивать кеширование с корректностью SQL-выборки.
Где xPDO-запросы ломаются
N+1 запросов в цикле
Типичная ошибка выглядит так: сначала вы получаете 50 ресурсов, затем для каждого вызываете отдельный поиск, TV или связанный объект. На странице появляется один запрос на список и десятки дополнительных. Сначала посмотрите SQL-лог и количество запросов, затем решите, можно ли получить данные одной выборкой или подготовить отдельную агрегированную выборку.
Слишком широкий объект
Запрос без ограничения полей может быть приемлем для одного ресурса, но дорог для каталога. Проверьте размер результата, число строк и наличие больших полей. Не оптимизируйте вслепую: замерьте страницу до и после на одинаковом наборе данных.
Неверные условия для NULL
Проверка на пустое значение в SQL не всегда равна сравнению с пустой строкой. Если поле может быть NULL, отдельно проверьте, как оно хранится в схеме и какое условие генерирует xPDO. Это место стоит проверить через SQL, который сформировал запрос, а не по одному результату в браузере.
Неправильная связь между объектами
Название таблицы и имя связи в карте модели — разные вещи. Если getOne или getMany возвращает пустой результат, проверьте карту класса и объявление связи в пакете. Не угадывайте имя по названию таблицы: в пользовательских моделях оно может отличаться.
SQL-инъекция через сортировку
Значения условий xPDO экранирует в рамках своего API, но имена полей, направление сортировки и фрагменты условий нельзя бездумно собирать из GET-параметров. Для сортировки используйте белый список: пользовательское значение должно превращаться в одно из заранее заданных имён полей.
Что проверить перед публикацией
- Запрос использует имя xPDO-класса и работает с префиксом таблиц проекта.
- Есть явные условия публикации, удаления и нужного контекста.
- Выбраны только поля, которые действительно нужны шаблону.
- Есть сортировка, ограничение и понятное поведение для пустого результата.
- Нет вызовов поиска связанных данных внутри цикла без замера количества запросов.
- Параметры пользователя не превращаются напрямую в имя поля, SQL-фрагмент или направление сортировки.
- Код проверен на MODX и xPDO той версии, которая установлена на проекте.
Как проверить запрос на проекте
Сначала зафиксируйте исходные условия: версию MODX, PHP, размер таблицы и число строк в результате. Затем включите доступный в вашей среде SQL-лог или профилировщик и сравните количество запросов, время выполнения и объём памяти для пустого, обычного и большого результата.
Для отладки полезно посмотреть сформированный SQL через средства отладки вашей версии xPDO, но не переносите такой диагностический вызов в рабочий вывод. Имена методов отладки и формат логов могут отличаться между xPDO 2.x и 3.x, поэтому сверяйте их с документацией конкретной версии.
Следующий шаг — добавить тестовый сценарий для максимального ожидаемого числа ресурсов и отдельно проверить страницу под авторизованным пользователем, в другом контексте и при пустом результате. Если запрос участвует в каталоге, после этого измерьте не только время ответа, но и число SQL-запросов на одну страницу.
1 xPDO — слой объектно-реляционного доступа MODX: он связывает PHP-классы модели с таблицами базы данных и строит запросы через объектный API.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: Официальный репозиторий xPDO, Документация MODX
Частые вопросы
Работает ли этот чек-лист в MODX 3?
Почему getCollection возвращает пустой результат?
Как понять, что в цикле появились лишние запросы?
Нужно ли всегда указывать published и deleted?
Можно ли передавать имя сортировки из URL?
Что делать, если пример не работает после обновления MODX?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.