📘 pbQuery
pbQuery — это мощная и лаконичная обёртка над xPDOQuery, вдохновлённая стилем Laravel Query Builder. Она позволяет писать читаемые, гибкие и безопасные запросы к базе данных в MODX.
Работать с pbQuery можно двумя способами:
Через фасад:
phppbQuery::table(modResource::class)Или через глобальный хелпер (предпочтительный способ):
phpquery(modResource::class)
Далее во всех примерах мы будем использовать именно хелпер query().
🏷 Изменение алиаса таблицы
По умолчанию pbQuery использует имя класса (например, modResource) как алиас таблицы во всех SQL-запросах:
query(modResource::class)🔎 SQL:
SELECT `modResource`.`id`, ... FROM `modx_site_content` AS `modResource`Если тебе нужно задать свой собственный алиас — используй метод alias():
query(modResource::class)
->alias('r')
->select(['id', 'pagetitle'])
->fetchAll();🔎 SQL:
SELECT `r`.`id`, `r`.`pagetitle` FROM `modx_site_content` AS `r`Алиас используется во всех частях запроса: SELECT, WHERE, JOIN, ORDER BY и т.д.
🔹 Получение всех записей
Чтобы получить все ресурсы из таблицы modResource:
$resources = query(modResource::class)->get();Метод get() возвращает итератор (Traversable) — вы можете безопасно перебирать результаты в foreach, даже если записей будет очень много:
foreach ($resources as $resource) {
echo $resource->pagetitle;
}Этот способ аналогичен нативному MODX:
$resources = $modx->getIterator(modResource::class);📌 get() vs all()
В pbQuery доступны два метода для получения множественных записей: get() и all(). Они оба возвращают список объектов, но отличаются тем, как они это делают.
| Метод | Что возвращает | Когда использовать |
|---|---|---|
get() | Traversable (ленивый итератор) | При большом объёме данных, для экономии памяти |
all() | array (массив объектов) | Когда нужно быстро работать со всеми объектами |
Пример с get() (итератор):
foreach (query(modResource::class)->get() as $resource) {
echo $resource->id;
}Пример с all() (массив):
$resources = query(modResource::class)->all();
echo count($resources); // Получаем количествоРазница важна: get() подгружает данные по мере итерации (экономия памяти), а all() сразу получает все данные в память. Если вы делаете сортировку, подсчёт или манипуляции с массивом — используйте all().
🔸 Получение одной записи
Первый результат
Чтобы получить первый найденный ресурс:
$resource = query(modResource::class)->first();Этот метод возвращает null, если запись не найдена. Он аналогичен $modx->getObject(...).
Последний результат
Метод last() автоматически делает сортировку по убыванию ID и возвращает последний объект:
$resource = query(modResource::class)->last();Случайный результат
Чтобы получить один случайный объект:
$resource = query(modResource::class)->rand();Полезно, например, для случайного баннера, отзыва или товара.
🔍 Поиск конкретной записи
По ID (по умолчанию)
$resource = query(modResource::class)->find(3);Этот код вернёт объект ресурса с id = 3.
По другому полю
Вы можете указать и другое поле:
$resource = query(modResource::class)->find('about', 'alias');Здесь будет найден ресурс, у которого alias = 'about'.
📋 Получение данных в виде массива
Если вам не нужны объекты xPDOObject, вы можете запросить данные в виде обычных массивов.
Все записи:
$resources = query(modResource::class)->fetchAll();Результат — массив, где каждая запись представлена как ассоциативный массив (аналогично PDO::FETCH_ASSOC).
Только одна запись:
$resource = query(modResource::class)->fetch();Возвращает первую найденную строку как массив.
🧷 Извлечение отдельных значений
Метод value()
Чтобы получить одно конкретное значение одного поля:
$alias = query(modResource::class)
->where(['id' => 6])
->value('alias');Возвращает, например: 'about'.
Метод pluck()
Метод pluck() позволяет получить массив значений из одного столбца:
$aliases = query(modResource::class)
->where(['published' => 1])
->pluck('alias');Результат:
['home', 'about', 'contacts', ...]Вы также можете указать, какое поле использовать в качестве ключа массива:
$aliases = query(modResource::class)
->pluck('alias', 'id');Результат:
[
1 => 'home',
2 => 'about',
3 => 'contacts',
]📊 Агрегации
pbQuery предоставляет удобные методы для выполнения агрегатных запросов к базе данных. Эти методы работают аналогично SQL-функциям COUNT, MIN, MAX, AVG, SUM, EXISTS, и упрощают их вызов.
🔢 count()
Возвращает количество записей, соответствующих условиям запроса.
$count = query(modResource::class)
->where(['published' => 1])
->count();👉 Вернёт, например: 42.
📉 min()
Получить минимальное значение поля:
$minId = query(modResource::class)->min('id');👉 Вернёт самое маленькое значение поля id.
📈 max()
Получить максимальное значение поля:
$maxId = query(modResource::class)->max('id');👉 Можно использовать, чтобы найти последний ID в таблице.
➗ avg()
Рассчитать среднее значение поля:
$avgRank = query(modUser::class)->avg('rank');👉 Вернёт среднее значение поля rank (например, 3.14).
➕ sum()
Получить сумму значений определённого поля:
$totalViews = query(modResource::class)
->where(['published' => 1])
->sum('views');👉 Вернёт, например: 15927.
📊 range()
Возвращает массив с минимальным и максимальным значением поля:
$range = query(modUser::class)->range('rank');👉 Результат:
[
'min' => 1,
'max' => 5,
]Полезно, например, чтобы построить слайдер с минимальным и максимальным значением.
❓ exists()
Проверяет, существует ли хотя бы одна запись, удовлетворяющая условиям:
$hasUnpublished = query(modResource::class)
->where(['published' => 0])
->exists();👉 Вернёт true или false.
🔗 Соединения таблиц
Методы соединения позволяют объединять текущую таблицу с другими таблицами или подзапросами по заданному условию.
📌 Методы
| Метод | Описание |
|---|---|
join($class, $alias, $on) | Выполняет INNER JOIN по условию ON. |
leftJoin($class, $alias, $on) | Выполняет LEFT JOIN. |
rightJoin($class, $alias, $on) | Выполняет RIGHT JOIN. |
joinSub($callback, $alias, $on) | Выполняет JOIN с подзапросом. |
leftJoinSub($callback, $alias, $on) | LEFT JOIN с подзапросом. |
rightJoinSub($callback, $alias, $on) | RIGHT JOIN с подзапросом. |
📎 Пример 1. Простой LEFT JOIN с пользователем
query(modResource::class)
->alias('r')
->leftJoin(modUser::class, 'u', 'r.createdby = u.id')
->select(['r.id', 'r.pagetitle', 'u.username'])
->fetchAll();🟢 SQL:
SELECT `r`.`id`, `r`.`pagetitle`, `u`.`username`
FROM `modx_site_content` AS `r`
LEFT JOIN `modx_users` AS `u` ON r.createdby = u.id📎 Пример 2. Несколько JOIN подряд
query(modResource::class)
->alias('r')
->select(['r.id', 'creator.username as created_by', 'editor.username as edited_by'])
->join(modUser::class, 'creator', 'r.createdby = creator.id')
->leftJoin(modUser::class, 'editor', 'r.editedby = editor.id')
->fetchAll();🟢 SQL:
SELECT `r`.`id`, `creator`.`username` AS `created_by`, `editor`.`username` AS `edited_by`
FROM `modx_site_content` AS `r`
JOIN `modx_users` AS `creator` ON r.createdby = creator.id
LEFT JOIN `modx_users` AS `editor` ON r.editedby = editor.id📎 Пример 3. LEFT JOIN с подзапросом (joinSub)
query(modUser::class)
->alias('u')
->select(['u.username', 'p.fullname'])
->leftJoinSub(function ($query) {
$query->table(modUserProfile::class)
->select(['internalKey', 'fullname']);
}, 'p', 'u.id = p.internalKey')
->fetchAll();🟢 SQL:
SELECT `u`.`username`, `p`.`fullname`
FROM `modx_users` AS `u`
LEFT JOIN (
SELECT `modUserProfile`.`internalKey`, `modUserProfile`.`fullname`
FROM `modx_user_attributes` AS `modUserProfile`
) AS p ON u.id = p.internalKey🔀 Объединение запросов
Методы union и unionAll позволяют объединить результаты нескольких запросов с одинаковой структурой столбцов в один набор данных.
📌 Методы
| Метод | Описание |
|---|---|
union($callback) | Объединяет с другим запросом через UNION (уникальные строки). |
unionAll($callback) | Объединяет с другим запросом через UNION ALL (все строки). |
📎 Пример 1. Объединение ресурсов с разными шаблонами
query(modResource::class)
->select(['id', 'pagetitle'])
->where(['template' => 1])
->union(function ($query) {
$query->table(modResource::class)
->select(['id', 'pagetitle'])
->where(['template' => 2]);
})
->fetchAll();🟢 SQL:
(SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`template` = 1)
UNION
(SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`template` = 2)📎 Пример 2. Объединение пользователей и администраторов (unionAll)
query(modUser::class)
->select(['id', 'username'])
->where(['sudo' => 0]) // обычные пользователи
->unionAll(function ($query) {
$query->table(modUser::class)
->select(['id', 'username'])
->where(['sudo' => 1]); // админы
})
->fetchAll();🟢 SQL:
(SELECT `modUser`.`id`, `modUser`.`username`
FROM `modx_users` AS `modUser`
WHERE `modUser`.`sudo` = 0)
UNION ALL
(SELECT `modUser`.`id`, `modUser`.`username`
FROM `modx_users` AS `modUser`
WHERE `modUser`.`sudo` = 1)📝 Особенности union
unionиunionAllпринимаютClosure, в который передаётся экземпляр$query, с которым нужно работать напрямую.- Количество и порядок колонок должны совпадать в каждом запросе.
- Все методы выборки поддерживаются:
fetch(),fetchAll(),get(),count(),toSql()и другие.
🧩 Where: фильтрация записей
Метод where() позволяет накладывать условия на выборку.
Поддерживаются:
- простые условия с массивом,
- вложенные условия через
Closure, - логические связки
AND/OR(второй параметр), - расширенные операторы для обычных и JSON-полей.
Методы:
where(array|Closure $conditions, string $conjunction = 'AND'): self
orWhere(array|Closure $conditions): self
whereRaw(string $expression): self
orWhereRaw(string $expression): self
whereColumn(array $conditions, string $conjunction = 'AND'): self
orWhereColumn(array $conditions): self
whereExists(\Closure $callback): self
whereNotExists(\Closure $callback): self✅ Пример
query(modResource::class)->where([
'template' => 1,
]);WHERE `template` = 1📘 Поддерживаемые операторы
| Оператор | Пример в PHP | SQL Result Example |
|---|---|---|
= (по умолчанию) | 'template' => 1 | template = 1 |
!= | 'template:!=' => 1 | template != 1 |
> | 'id:>' => 10 | id > 10 |
< | 'id:<' => 10 | id < 10 |
>= | 'id:>=' => 5 | id >= 5 |
<= | 'id:<=' => 5 | id <= 5 |
LIKE | 'pagetitle:LIKE' => 'demo' | pagetitle LIKE '%demo%' |
LIKE + массив | 'pagetitle:LIKE' => ['one', 'two'] | (pagetitle LIKE '%one%' OR pagetitle LIKE '%two%') |
NOT LIKE | 'title:NOT LIKE' => 'test' | title NOT LIKE '%test%' |
NOT LIKE + массив | 'title:NOT LIKE' => ['a', 'b'] | (title NOT LIKE '%a%' AND title NOT LIKE '%b%') |
IN | 'id:IN' => [1, 2, 3] | id IN (1, 2, 3) |
NOT IN | 'id:NOT IN' => [1, 2, 3] | id NOT IN (1, 2, 3) |
IS | 'deleted:IS' => null | deleted IS NULL |
IS NOT | 'deleted:IS NOT' => null | deleted IS NOT NULL |
BETWEEN | 'id:BETWEEN' => [10, 20] | id >= 10 AND id <= 20 |
FIND_IN_SET | 'groups:FIND_IN_SET' => 3 | FIND_IN_SET(3, groups) |
FIND_NOT_IN_SET | 'groups:FIND_NOT_IN_SET' => 3 | NOT FIND_IN_SET(3, groups) |
✅ Простейшие условия
query(modResource::class)->where([
'published' => 1,
'template' => 2,
])->get();SQL:
SELECT * FROM modx_site_content WHERE published = 1 AND template = 2🔁 OR вместо AND (через второй аргумент)
query(modResource::class)->where([
'template' => 1,
'parent' => 0,
], 'OR')->get();SQL:
SELECT * FROM modx_site_content WHERE template = 1 OR parent = 0🧠 Смешанные ключи с OR: (внутри одного массива)
query(modResource::class)
->select(['id', 'pagetitle'])
->where([
'published' => 1,
'OR:pagetitle:LIKE' => '%demo%',
'OR:alias:LIKE' => '%demo%',
])->get();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE (
`modResource`.`published` = 1
OR `modResource`.`pagetitle` LIKE '%demo%'
OR `modResource`.`alias` LIKE '%demo%')🧩 orWhere() как альтернатива
query(modResource::class)
->where(['published' => 1])
->orWhere([
'template' => 1,
'parent' => 0,
])
->get();То же самое, что:
->where([
'template' => 1,
'parent' => 0,
], 'OR')🔄 Составной массив условий
query(modResource::class)->orWhere([
['parent' => 12, 'published' => 1],
['template' => 7, 'published' => 1],
])->get();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE (
(`modResource`.`parent` = 12 AND `modResource`.`published` = 1) OR
(`modResource`.`template` = 7 AND `modResource`.`published` = 1)
)🔗 Подзапросы в where
Метод where() поддерживает подзапросы через передачу замыкания (анонимной функции). Это удобно для условий с IN, NOT IN, EXISTS и NOT EXISTS.
📌 Поддерживаемые операторы
| Условие | Пример ключа в where | SQL-результат |
|---|---|---|
IN | 'id:IN' => fn ($q) => ... | id IN (SELECT ...) |
NOT IN | 'id:NOT IN' => fn ($q) => ... | id NOT IN (SELECT ...) |
EXISTS | ':EXISTS' => fn ($q) => ... | EXISTS (SELECT ...) |
NOT EXISTS | ':NOT EXISTS' => fn ($q) => ... | NOT EXISTS (SELECT ...) |
✅ Пример: найти ресурсы, у которых id входит в другой выбор
query(modResource::class
->select(['id', 'pagetitle'])
->where([
'id:IN' => function ($query) {
$query
->select('id')
->where(['template' => 7]);
}
])
->fetchAll();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE (`modResource`.`id` IN (
SELECT `modResource`.`id`
FROM `modx_site_content` AS `modResource`
WHERE (`modResource`.`template` = 7)
))✅ Пример: ресурсы, у которых нет потомков
query(modResource::class)
->select(['id', 'pagetitle'])
->where([
'id:NOT IN' => function ($query) {
$query
->select('parent')
->where(['parent:!=' => 0]);
}
])
->fetchAll();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE (`modResource`.`id` NOT IN (
SELECT `modResource`.`parent`
FROM `modx_site_content` AS `modResource`
WHERE (`modResource`.`parent` != 0)
))✅ Пример: с EXISTS
query(\modResource::class)
->select('id,pagetitle')
->where([':EXISTS' => function ($query) {
$query->table(\modTemplate::class)
->select('id')
->where(['category' => 53]);
}
])->get();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE EXISTS (
SELECT `modTemplate`.`id`
FROM `modx_site_templates` AS `modTemplate`
WHERE (`modTemplate`.`category` = 53) )🧨 Сырые SQL условия
query(modResource::class)
->select('id,pagetitle')
->whereRaw('pagetitle LIKE "%demo%"')
->orWhereRaw('alias LIKE "%demo%"')
->get();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE ( pagetitle LIKE "%demo%" OR alias LIKE "%demo%" )🔍 Поиск по JSON-полям
pbQuery поддерживает удобную работу с данными, хранящимися в JSON-формате — например, в поле values.
📦 Автоматический режим
Если вы указываете условие where, а поле отсутствует в таблице, pbQuery автоматически ищет его в JSON-поле (по умолчанию — values).
query(modResource::class)
->where(['priority:>' => 5]) // ищет в values->priority
->get();- Не нужно указывать
values->priority—pbQueryсам это сделает. - Работают все стандартные операторы:
=,LIKE,IN,>,<,!=и др.
📌 Явное указание JSON-поля
Если вы хотите указать точный путь к JSON-ключу, используйте синтаксис с ->. Это может быть полезно, если вы работаете с несколькими JSON-полями или хотите задать глубину.
query(modResource::class)
->where([
'values->brand' => 'adidas',
'values->price:>=' => 100,
'data->meta->color:LIKE' => '%red%',
])В этом случае
pbQueryпонимает, что это доступ к JSON, и автоматически подставитJSON_EXTRACTиJSON_UNQUOTE.
🔢 Умное приведение типов
Если значение — число (int или float), то сравнение будет выполнено как числовое (CAST(...) AS DECIMAL), иначе как строка.
->where(['values->discount:>' => 0]) // сравнение как с числом
->where(['values->discount' => '0']) // сравнение как со строкой🔎 Особые операторы
Некоторые операторы требуют особой обработки и дают больше гибкости:
✅ SEARCH
Ищет подстроку в любом месте JSON независимо от вложенности. Используется через основной JSON-путь (values->...), но работает "вглубь".
->where(['meta:SEARCH' => 'поиск']) // внутри values->meta- Регистр не имеет значения.
- Можно передать массив значений:
->where(['meta:SEARCH' => ['один', 'два']])✅ JSON_CONTAINS
Проверяет, что JSON-массив содержит указанное значение (или все значения из массива).
->where(['tags:JSON_CONTAINS' => 'news'])
->where(['tags:JSON_CONTAINS' => ['news', 'featured']])✅ NOT JSON_CONTAINS
Отрицание JSON_CONTAINS — значение не должно быть найдено в массиве.
->where(['tags:NOT JSON_CONTAINS' => 'old'])💡 Примеры
query(modResource::class)
// Автоматический JSON (values->priority)
->where(['priority:>' => 5])
// Явное указание JSON-пути
->where(['values->brand' => 'nike'])
// Числовое сравнение
->where(['values->discount:>' => 0])
// Поиск по вложенным ключам
->where(['data->meta->price:<=' => 1000])
// LIKE
->where(['title:LIKE' => '%тест%'])
// IN
->where(['status:IN' => ['draft', 'published']])
// JSON_CONTAINS
->where(['tags:JSON_CONTAINS' => 'special'])
// SEARCH
->where(['meta:SEARCH' => 'текст'])
// Комбинированный поиск
->where([
['meta:SEARCH' => 'один'],
['type:IN' => ['a', 'b']],
])🔗 whereColumn() и orWhereColumn()
Методы whereColumn() и orWhereColumn() позволяют сравнивать одно поле с другим полем, а не со значением. Это полезно, когда тебе нужно сравнение между двумя колонками, например createdby = editedby или publishedon > editedon.
->whereColumn(array $conditions, string $conjunction = 'AND'): self
->orWhereColumn(array $conditions): self📌 Синтаксис
query(modResource::class)
->whereColumn([
'createdby' => 'editedby',
'publishedon:>' => 'editedon',
])
->fetchAll();- Ключ — это имя поля (возможно с оператором, например
publishedon:>). - Значение — поле, с которым нужно сравнивать.
- Поддерживаются любые SQL-операторы:
=,!=,<,>,<=,>=,LIKE,NOT LIKE, и т.д.
📌 orWhereColumn()
То же самое, но с использованием OR вместо AND.
query(modResource::class)
->orWhereColumn([
'createdby' => 'editedby',
'deletedon:<=' => 'publishedon'
])
->fetchAll();🧠 Пример: сравнение с другой таблицей
Если ты используешь join(), можно сравнивать поля из разных таблиц:
query(modResource::class)
->select('id,pagetitle,createdby')
->join(modUser::class, 'User', 'User.id = modResource.createdby')
->whereColumn([
'modResource.createdby' => 'User.id'
])
->fetchAll();🔎 SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`, `modResource`.`createdby`
FROM `modx_site_content` AS `modResource`
JOIN `modx_users` AS `User`
ON User.id = modResource.createdby
WHERE `modResource`.`createdby` = `User`.`id`🔍 whereExists()
Метод whereExists() добавляет условие EXISTS (...) в SQL-запрос. Используется для вложенных запросов, которые проверяют наличие записей, соответствующих условиям.
query(modResource::class)
->select('id,pagetitle')
->whereExists(function ($q) {
$q->table(\modTemplate::class)
->select('id')
->where(['category' => 53]);
});
->fetchAll();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE EXISTS (
SELECT `modTemplate`.`id`
FROM `modx_site_templates` AS `modTemplate`
WHERE (`modTemplate`.`category` = 53) )📌 whereNotExists()
Метод whereNotExists() добавляет в запрос условие NOT EXISTS (...). Он противоположен whereExists() и используется для исключения записей, у которых не существует связанных данных.
Пример:
query(modResource::class)
->select('id,pagetitle')
->whereNotExists(function ($q) {
$q->table(modTemplate::class)
->select('id')
->where('category', 53);
})
->fetchAll();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE NOT EXISTS (
SELECT `modTemplate`.`id`
FROM `modx_site_templates` AS `modTemplate`
WHERE (`modTemplate`.`category` = 53) )🎯 Выбор полей
Методы select(), selectRaw() и distinct() позволяют гибко управлять выборкой полей в pbQuery.
📌 select()
Метод select() задаёт список полей, которые нужно получить из базы данных. Поддерживает строки, массивы, алиасы, JSON-поля, подзапросы (Closure) и множественные вызовы.
▪️ Строка
query(modResource::class)
->select('id, pagetitle')
->fetchAll();▪️ Массив
query(modResource::class)
->select(['id', 'pagetitle'])
->fetchAll();▪️ Алиасы (AS)
query(modResource::class)
->select(['pagetitle as title']) // или ['pagetitle' => 'title']
->fetchAll();🧩 Поддержка JSON-полей
Если указанное поле не существует в таблице, оно будет автоматически извлечено из JSON-колонки values:
query(modResource::class)
->select(['id', 'price']) // => values->price
->fetchAll();Можно явно указать JSON-путь через ->:
query(modResource::class)
->select('properties->title as prop_title')
->select('values->description')
->get();query(modResource::class)
->select('id, pagetitle, properties->seosuite->uri as seouri')
->fetchAll();🗂 JSON-ключи поддерживаются с алиасами (
AS) и без.
🔁 Множественные вызовы
Метод select() можно вызывать несколько раз — поля объединяются:
query(modResource::class)
->select('id')
->select(['pagetitle', 'price'])
->get();🔀 Подзапросы в select() через Closure
Метод select() поддерживает Closure, позволяя создавать подзапросы внутри SELECT:
query(modResource::class)
->alias('resource')
->select('id, pagetitle')
->select(['children_count' => function ($q) {
$q->selectRaw('COUNT(*)')
->whereColumn(['parent' => 'resource.id']);
}])
->fetchAll();SQL:
SELECT `resource`.`id`, `resource`.`pagetitle`, (
SELECT COUNT(*)
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`parent` = `resource`.`id` ) as `children_count`
FROM `modx_site_content` AS `resource`🔢 Подсчёт через selectCount()
Упрощённая версия подзапроса COUNT(*) с условиями:
query(modResource::class)
->alias('resource')
->select('id, pagetitle')
->selectCount('children_count', [
'parent' => 'resource.id'
])
->fetchAll();🧪 Произвольный SQL: selectRaw()
Добавляет любое выражение или подзапрос вручную:
query(modResource::class)
->alias('resource')
->select('id, pagetitle')
->selectRaw('(
SELECT COUNT(*)
FROM modx_site_content
WHERE parent = resource.id
) as children_count')
->fetchAll();🔁 distinct()
Метод distinct() исключает повторяющиеся строки:
query(modResource::class)
->select('createdby')
->distinct()
->fetchAll();SQL
SELECT DISTINCT `modResource`.`createdby`
FROM `modx_site_content` AS `modResource`👉 Получим список всех уникальных авторов.
🔃 Сортировка
orderBy(field, direction = 'ASC')
Сортирует по указанному полю. Если поле отсутствует в таблице — предполагается, что оно находится в JSON-поле values.
query(modResource::class)
->orderBy('menuindex') // по умолчанию ASC
->get();query(modResource::class)
->orderBy('price', 'DESC') // JSON-поле values->price
->get();📌 Сортировка по id_order
Если в where() задан id:IN, метод:
query(modResource::class)
->where(['id:IN' => '3,7,1'])
->orderBy('id_order')
->get();вернёт записи в том порядке, как указано: [3, 7, 1].
💡 Передача Closure в orderBy
Можно задать более сложную сортировку через замыкание:
query(modResource::class)
->select('id,pagetitle')
->orderBy(function ($q) {
$q->table(\modUser::class)
->select('createdon')
->whereColumn([
'modUser.id' => 'modResource.createdby'
]);
}, 'DESC')
->fetchAll();SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
ORDER BY (
SELECT `modUser`.`createdon`
FROM `modx_users` AS `modUser`
WHERE `modUser`.`id` = `modResource`.`createdby`
) DESC🔁 Сортировка по числу дочерних элементов:
query(\modResource::class)
->alias('resource')
->select('id,pagetitle')
->selectCount('children_count', [
'parent' => 'resource.id'
])
->orderBy(function ($q) {
$q->table(\modResource::class)
->selectRaw('COUNT(*)')
->whereColumn([
'parent' => 'resource.id'
]);
}, 'DESC')
->fetchAll();SQL:
SELECT `resource`.`id`, `resource`.`pagetitle`, (SELECT COUNT(*)
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`parent` = `resource`.`id`
) as `children_count`
FROM `modx_site_content` AS `resource`
ORDER BY (
SELECT COUNT(*)
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`parent` = `resource`.`id`
) DESCArray
(
[0] => Array
(
[id] => 47
[pagetitle] => Очень большая
[children_count] => 34
)
[1] => Array
(
[id] => 49
[pagetitle] => Нецелые цены
[children_count] => 24
)
[2] => Array
(
[id] => 12
[pagetitle] => Женская одежда
[children_count] => 23
)
[3] => Array
(
[id] => 1
[pagetitle] => Детские товары
[children_count] => 17
)
)🧪 orderByRaw(expression)
Позволяет передать произвольное SQL-выражение в ORDER BY:
query(modResource::class)
->orderByRaw("FIELD(id, 7, 3, 1)")
->get();query(modResource::class)
->orderByRaw("CASE WHEN template = 1 THEN 0 ELSE 1 END")
->get();🧮 Группировка
groupBy(field)
Группирует результаты по полю:
query(modResource::class)
->select(['id','template'])
->groupBy('template')
->fetchAll();🎯 Фильтрация групп
having(string $expression)
query(modResource::class)
->alias('resource')
->select('template')
->selectRaw('COUNT(id) as total')
->groupBy('template')
->having('total > 5')
->fetchAll();SQl:
SELECT `resource`.`template`, COUNT(id) as total
FROM `modx_site_content` AS `resource`
GROUP BY template
HAVING total > 5Результат:
Array
(
[0] => Array
(
[template] => 0
[total] => 172
)
[1] => Array
(
[template] => 1
[total] => 62
)
[2] => Array
(
[template] => 7
[total] => 17
)
)Выберем шаблоны (template), у которых количество ресурсов находится в пределах от 5 до 10 включительно:
query(modResource::class)
->groupBy('template')
->having('COUNT(id) BETWEEN 5 AND 10')
->fetchAll();📌 Ограничения
limit(n)— ограничивает количество результатовoffset(n)— пропускает первыеnзаписей
query(modResource::class)
->limit(10)
->offset(20)
->get();👉 Получим 10 записей, начиная с 21-й.
✨ Пагинация
paginate(int $perPage = 10, string $pageName = 'page'): Paginator
Метод возвращает объект Paginator для удобного постраничного вывода данных.
Параметры
$perPage(int, по умолчанию10) Количество записей на одной странице.$pageName(string, по умолчанию'page') Название параметра в query string, который указывает текущую страницу. Например, если$pageName = 'p', то URL будет вида:html/products?p=2
Пример использования в шаблоне
{set $users = query('modUser')
->where(['active' => 1])
->paginate(15)}
<ul>
{foreach $users->items() as $user}
<li>{$user.username}</li>
{/foreach}
</ul>
<div class="pb-pagination" pb-pagination>
{$users->links()}
</div>Дополнительно
Подробнее о доступных методах и возможностях смотрите в документации класса Paginator.
✏️ Изменение данных
Методы изменения данных в pbQuery:
| Метод | Использует | Описание |
|---|---|---|
create | xPDO | Создаёт один или несколько объектов через newObject()->save() |
update | xPDO | Обновляет поля у найденных объектов |
remove | xPDO | Удаляет найденные объекты |
increment | xPDO | Увеличивает числовое поле |
decrement | xPDO | Уменьшает числовое поле |
firstOrCreate | xPDO | Если запись существует — возвращает, иначе создаёт новую |
updateOrCreate | xPDO | Если запись существует — обновляет, иначе создаёт новую |
insert | SQL | Вставляет данные напрямую в таблицу без использования моделей |
command('UPDATE') | SQL | Выполняет массовое обновление через чистый SQL UPDATE |
command('DELETE') | SQL | Выполняет массовое удаление через чистый SQL DELETE |
✅ create(array $data)
Создаёт одну или несколько записей через xPDO. Возвращает:
xPDOObjectпри успешном создании одной записи,null— если не удалось создать,7- количество созданных записей — при массовом создании.
// Создание одной записи
query(modResource::class)->create([
'pagetitle' => 'Новый ресурс',
'context_key' => 'web'
]);
// Создание нескольких записей
query(modResource::class)->create([
['pagetitle' => 'Новый ресурс', 'context_key' => 'web'],
['pagetitle' => 'Новый ресурс2', 'context_key' => 'web'],
]);💡 Использует
$modx->newObject()и$object->save()для каждой записи.
🧩 insert(array $data): int
Вставка данных напрямую в таблицу через SQL (без xPDO). Работает быстрее create, но не вызывает save()-событий.
query(modResource::class)->insert([
'pagetitle' => 'Новый ресурс',
'context_key' => 'web'
]);
query(modResource::class)->insert([
['pagetitle' => 'Новый ресурс', 'context_key' => 'web'],
['pagetitle' => 'Новый ресурс2', 'context_key' => 'web'],
]);Возвращает
lastInsertId()при одной записи, либоrowCount()при множественной вставке.
🔄 update(array $data): int
Обновляет поля найденных объектов.
query(modResource::class)
->where(['parent' => 2])
->update(['deleted' => 1]);Возвращает количество обновлённых записей.
🗑 remove(): int
Удаляет все найденные объекты.
query(modResource::class)
->where(['parent' => 2])
->remove();Возвращает количество удалённых объектов.
📈 increment(string $column, int $count = 1): bool
Увеличивает значение числового поля.
pbQuery::table(modResource::class)
->where(['parent' => 2])
->increment('menuindex');📉 decrement(string $column, int $count = 1): bool
Уменьшает значение поля. То же самое, что increment(..., -$count).
pbQuery::table(modUserProfile::class)
->where(['parent' => 2])
->decrement('menuindex');🧩 firstOrCreate(array $values): ?\xPDOObject
Если по текущему запросу уже существует объект — он будет возвращён. Если нет — будет создан новый объект с указанными значениями.
$user = query(modUser::class)
->where(['username' => 'admin'])
->firstOrCreate([
'username' => 'admin',
'password' => md5('123456'),
]);Если пользователь
adminуже существует — вернётся он. Если нет — будет создан с заданным паролем.
🔁 updateOrCreate(array $data): int
Если существует запись по текущему запросу — обновляет, иначе создаёт.
pbQuery::table(modSystemSetting::class)
->where(['key' => 'site_name'])
->updateOrCreate(['value' => 'New Site Title']);Возвращает количество обновлённых или созданных записей.
🛠 command(string $command = 'SELECT'): self
Позволяет выполнять низкоуровневые SQL-команды UPDATE и DELETE напрямую. Используется в связке с set() (для UPDATE) и where().
🔧 В отличие от
update()иremove(), работает напрямую через SQL, минуя xPDO. Подходит для массовых изменений без вызова событийsave()/remove().
✅ Пример: обновление записей через UPDATE
$result = query(modResource::class)
->command('UPDATE')
->set([
'deleted' => 0,
'published' => 1,
])
->where([
'template' => 5,
'parent' => 2,
])
->execute();🔸 SQL:
UPDATE `modx_site_content`
SET `deleted` = 0, `published` = 1
WHERE `template` = 5 AND `parent` = 2🗑 Пример: удаление записей через DELETE
$result = query(modResource::class)
->command('DELETE')
->where([
'published' => 0,
'parent' => 10,
])
->execute();🔸 SQL:
DELETE FROM `modx_site_content`
WHERE `published` = 0 AND `parent` = 10📌 Отличие от update() и remove()
| Метод | Основа | События | Массово | Быстродействие |
|---|---|---|---|---|
update() | xPDO | ✅ save() | ✅ | 🐌 Медленно |
command('UPDATE') | SQL | ❌ | ✅ | ⚡ Быстро |
remove() | xPDO | ✅ remove() | ✅ | 🐌 Медленно |
command('DELETE') | SQL | ❌ | ✅ | ⚡ Быстро |
🔁 Перебор и обработка записей
Методы map, each, и chunk позволяют удобно обрабатывать выборки — как целиком, так и порциями. Это особенно полезно, когда нужно изменить, перебрать или извлечь данные из множества записей.
🔹 map(callable $callback): array
Метод map перебирает объекты из выборки и возвращает массив результатов, полученных после обработки каждого элемента.
💡 Когда использовать: Когда тебе нужно преобразовать коллекцию объектов в простой массив, например для API, списка ID, имён, и т.д.
// Получить массив ID всех опубликованных ресурсов
$ids = query(modResource::class)
->where('published', 1)
->map(fn($resource) => $resource->get('id'));
// Получить массив URL ресурсов
$urls = query(modResource::class)
->where('published', 1)
->map(function ($resource) {
return $resource->get('uri');
});🔹 each(callable $callback): self
Метод each перебирает объекты один за другим и передаёт их в коллбэк. Может использоваться для изменения данных, логирования, отправки писем и т.д.
💡 Когда использовать: Когда тебе нужно выполнить действие с каждым объектом, не создавая массив результата.
// Установить шаблон всем дочерним страницам
query(modResource::class)
->where(['parent' => 2])
->each(function ($resource) {
$resource->set('template', 5);
$resource->save();
})
->get();
// Записать лог по каждому ресурсу
query(modResource::class)
->where('published', 1)
->each(function ($resource) {
logger('Обработка ресурса: ' . $resource->get('pagetitle'));
})
->get();🔹 chunk(int $size, callable $callback): self
Метод chunk загружает объекты порциями и передаёт их в коллбэк. Это позволяет обрабатывать большие выборки без переполнения памяти.
💡 Когда использовать: При обработке тысяч записей, где важна экономия памяти и стабильность.
// Обработать ресурсы по 100 штук
query(modResource::class)
->where('template', 4)
->chunk(100, function ($resources) {
foreach ($resources as $resource) {
$resource->set('published', 0);
$resource->save();
}
});
// Отправить письма всем пользователям по 50 за раз
query(modUser::class)
->chunk(50, function ($users) {
foreach ($users as $user) {
$user->sendEmail("Привет, {$user->username}", [
'subject' => 'Есть кто живой?'
])
}
});Сравнение
| Метод | Что делает | Возвращает | Когда использовать |
|---|---|---|---|
map | Перебирает объекты и возвращает результат | Массив | Преобразование данных |
each | Перебирает объекты и выполняет действия | pbQuery | Изменение, логирование, вызов побочных эффектов |
chunk | Делит выборку на части и обрабатывает их | pbQuery | Массовая обработка без перегрузки памяти |
📦 Рендеринг шаблонов
Методы tpl(), tplWrapper(), tplParent, outputSeparator() и render() позволяют не только выполнить запрос, но и сразу отрендерить полученные данные через шаблоны. Это особенно удобно, если вы хотите сформировать HTML-вывод непосредственно из PHP без ручной вставки шаблонов в Fenom.
🔧 tpl(string $tpl): self
Устанавливает основной шаблон, который будет применяться к каждому элементу выборки.
$output = query(modResource::class)
->where(['published' => 1])
->tpl('tpl.resource.row')
->render();Пример шаблона tpl.resource.row.tpl:
<div class="item">
<h3>{$pagetitle}</h3>
<p>{$introtext}</p>
</div>🧩 tplWrapper(string $tpl, string $var = 'output'): self
Устанавливает шаблон-обёртку, которая применяется ко всему списку целиком. В обёртку передаётся переменная (по умолчанию output), содержащая весь отрендеренный список.
$output = pbQuery::table(modResource::class)
->tpl('tpl.resource.row')
->tplWrapper('tpl.resource.wrapper', 'list')
->render();Пример шаблона tpl.resource.wrapper.tpl:
<section class="resources">
{$list}
</section>🔹 tplParent(string $name): self
Шаблон, который используется только для элементов, у которых есть потомки(children). Это позволяет задать другой шаблон для родительских узлов дерева.
->tplParent('parent-item')Если tplParent не задан, то используется tplWrapper (если он задан) или основной tpl.
🔀 outputSeparator(string $separator): self
Позволяет задать разделитель между элементами списка. По умолчанию \n;
$output = pbQuery::table(modUser::class)
->tpl('tpl.user.row')
->outputSeparator("\n")
->render();🧪 render(): string
Выполняет запрос, применяет шаблон к каждому элементу, добавляет системные переменные и возвращает итоговую HTML-строку.
Каждому элементу передаются дополнительные переменные:
| Переменная | Значение |
|---|---|
_iteration | Порядковый номер элемента, начиная с 1 |
_idx | Индекс элемента, начиная с 0 |
_total | Общее количество элементов |
_first | true, если элемент первый |
_last | true, если элемент последний |
_even | true, если индекс чётный (0, 2, 4…) |
_odd | true, если индекс нечётный (1, 3, 5…) |
Пример шаблона tpl.user.row.tpl с использованием этих переменных:
<div class="user {if $_even}even{else}odd{/if}">
<span class="number">#{$_iteration}</span>
<strong>{$username}</strong>
</div>💡 Полный пример использования
echo query(modResource::class)
->where(['template' => 3, 'published' => 1])
->orderBy('menuindex')
->tpl('tpl.resource.row')
->tplWrapper('tpl.resource.wrapper', 'content')
->outputSeparator(PHP_EOL)
->render();📚 Метод menu
Метод menu() в pbQuery позволяет удобно строить древовидные структуры для вывода меню, категорий и других иерархических данных.
Пример использования
$menu = query(modResource::class)
->where(['published' => 1, 'hidemenu' => 0])
->tpl('tpl.menu.item')
->tplParent('tpl.menu.parent')
->tplWrapper('tpl.menu.wrapper')
->outputSeparator("\n")
->menu()$menu = query(modResource::class)
->alias('resource')
->select('id,pagetitle,alias,parent,menuindex')
->select(['count' => function ($q) {
$q->selectRaw('COUNT(*)')
->whereColumn(['parent' => 'resource.id']);
}])
->tplWrapper('@INLINE <ul>{$output}</ul>')
->tplParent('@INLINE <li>{$pagetitle} ({$count})</li><ul>{$output}</ul>')
->tpl('@INLINE <li>{$pagetitle}</li>')
->menu(0,2);Аргументы
menu(
int $rootId = 0,
int $maxLevel = 3,
string $parentField = 'parent',
string $primaryKey = 'id'
)$rootId— ID корневого элемента.$maxLevel— максимальная глубина вложенности.$parentField— имя поля, указывающего на родителя.$primaryKey— имя поля с первичным ключом.
Что делает
- Автоматически выставляет поля для выборки, если они ещё не заданы.
- Получает все элементы с помощью
fetchAll(). - Группирует их по родительскому полю (
parentField). - Строит иерархию методом
buildTree(). - Вызывает
render()для вывода через Fenom-шаблоны. - Если шаблон не задан — возвращает просто массив дерева.
Настройка шаблонов
Методы для шаблонов:
tpl($tpl)— шаблон обычного элемента.tplParent($tpl, $var = 'output')— шаблон родительского элемента,$var— имя переменной с вложенным выводом.tplWrapper($tpl, $var = 'output')— внешний обёрточный шаблон.outputSeparator($sep)— разделитель между элементами.
Пример без шаблонов
Если не заданы шаблоны — метод menu() вернёт массив дерева:
$tree = query(modResource::class)
->where(['published' => 1])
->menu();
return $tree;Структура выходного массива
[
[
'id' => 1,
'pagetitle' => 'Главная',
// ...
'level' => 1
'children' => [
[
'id' => 2,
'pagetitle' => 'О нас',
// ...
'level' => 2
'children' => []
]
]
]
]🌐 Метод languages
Метод languages() в pbQuery используется для вывода доступных языков сайта, а также генерации соответствующих URL для каждого языка.
Пример использования
query(modResource::class)
->where(['id' => $modx->resource->id])
->tplWrapper('@INLINE <nav>{$output}</nav>')
->tpl('@INLINE <a href="{$url}" class="{if $active}active{/if}">{$language}</a>')
->languages();Если шаблоны не заданы — вернёт массив с языками и ссылками.
Что делает
Получает текущий ресурс (
$this->first()).Загружает список контекстов из опции
pageblocks_contexts(в JSON).При активной опции
pageblocks_context_fallback != 'default'исключает те языки, для которых нет перевода ресурса.Формирует массив языков:
key— ключ контекста (web,uk,enи т.д.).value— название языка.active— активен ли текущий язык.language— синонимvalue.url— URL для переключения на язык.
Пример вывода без шаблонов
$languages = pbQuery::table(modResource::class)
->where(['id' => $modx->resource->id])
->languages();Пример результата:
[
[
'key' => 'web',
'value' => 'English',
'active' => true,
'language' => 'English',
'url' => '/',
],
[
'key' => 'uk',
'value' => 'Ukraine',
'active' => false,
'language' => 'Ukraine',
'url' => '/uk/',
],
[
'key' => 'ru',
'value' => 'Russian',
'active' => false,
'language' => 'Russian',
'url' => '/ru/',
]
]Настройка шаблонов
Метод languages() использует те же шаблонные методы, что и menu():
tpl($tpl)— шаблон одного элемента.tplWrapper($tpl, $var = 'output')— внешний обёртка.outputSeparator($sep)— разделитель между языками.
Требуемые настройки в MODX
В системных настройках должны быть заданы:
pageblocks_contexts— список контекстов в JSON:
[
{"key": "web", "value": "English"},
{"key": "uk", "value": "Ukraine"},
{"key": "ru", "value": "Russian"}
]🔍 Фильтрация результатов (filter)
Метод filter(callable $callback) позволяет отфильтровать уже полученные данные после выборки из базы, но до рендеринга шаблонов.
public function filter(callable $callback): self📌 Где используется
Метод filter() можно использовать с любыми методами, возвращающими массивы: ->menu(), ->languages(), ->all(), ->fetchAll().
📥 Параметры
$callback— функция обратного вызова, в которую передаётся каждый элемент массива. Если функция возвращаетtrue, элемент остаётся; еслиfalse— удаляется.
✅ Пример: фильтрация языков
Выводим список языков, кроме активного:
$languages = query('modResource')
->where(['id' => $modx->resource->id])
->tplWrapper('@INLINE <nav class="nav">{$output}</nav>')
->tpl('@INLINE <a class="nav-link" href="{$url}">{$language}</a>')
->filter(fn($item) => !$item['active'])
->languages();✅ Пример: фильтрация дерева по template
Выводим дерево ресурсов, исключая те, у которых template = 3:
$tree = query('modResource')
->filter(fn($item) => $item['template'] != 3)
->menu();⚙️ Доступ к объекту xPDOQuery
Иногда может понадобиться напрямую получить или подменить внутренний объект xPDOQuery, с которым работает pbQuery. Для этого предусмотрены методы getQuery() и setQuery().
🔹 getQuery(): xPDOQuery
Возвращает текущий экземпляр xPDOQuery, используемый внутри запроса. Это полезно, если ты хочешь выполнить низкоуровневые операции, которые не покрываются методом pbQuery.
💡 Когда использовать:
- При необходимости добавить сложное условие или выражение.
- При интеграции с внешними компонентами, которым нужен
xPDOQuery.
$query = query(modResource::class)
->where(['parent' => 10])
->getQuery();
$query->where([
'pagetitle:LIKE' => '%новость%',
]);
// Можно вручную получить результаты
$results = $query->prepare() && $query->stmt->execute()
? $query->stmt->fetchAll(PDO::FETCH_ASSOC)
: [];🔹 setQuery(xPDOQuery $query): self
Позволяет вручную установить объект xPDOQuery, заменив текущий. Это даёт полный контроль над логикой построения запроса — можно собрать его вне pbQuery, а затем использовать удобства класса (например, get(), first(), map() и т.д.).
💡 Когда использовать:
- Когда нужно предварительно собрать
xPDOQueryвручную или в другом месте. - Когда удобно сконструировать базовый запрос и передать его в
pbQuery.
$xpdoQuery = $modx->newQuery(modUser::class);
$xpdoQuery->where(['active' => 1]);
$users = query(modUser::class)
->setQuery($xpdoQuery)
->map(fn($user) => $user->get('username'));Эти методы дают возможность сочетать гибкость xPDO и удобство pbQuery. Их стоит использовать в продвинутых случаях, когда стандартных методов уже недостаточно.
🧠 Кеширование запросов
Метод cache() позволяет кешировать результат запроса и повторно использовать его при следующем вызове, избегая повторного выполнения SQL-запроса. Это особенно полезно для тяжёлых или часто повторяющихся операций.
🔹 cache(int $time = 3600, string $key = ''): self
- $time — время хранения кеша в секундах. По умолчанию 1 час (3600 секунд).
- $key — (необязательно) ключ кеша. Если не указан, будет сгенерирован автоматически на основе SQL.
Внутри используется метод Cache::remember(), который сохраняет результат выполнения, если его ещё нет в кеше, и возвращает сохранённый при следующем запросе.
$resources = query(modResource::class)
->where(['template' => 5])
->cache('', 600) // Кешируем на 10 минут
->fetchAll();🧪 Пример: кеширование с map()
$titles = query(modResource::class)
->where(['published' => 1])
->cache('', 300) // 5 минут
->map(fn($res) => $res->get('pagetitle'));💡 Зачем использовать кеширование
- Снижение нагрузки на базу данных.
- Ускорение времени ответа.
- Удобство использования без необходимости вручную управлять кешем.
🐞 Отладка запросов
Чтобы увидеть, какой SQL-запрос выполняется, сколько он занял времени, сколько памяти использовано и была ли задействована система кеширования — используйте метод debug().
🔹 debug(): self
Включает вывод отладочной информации после выполнения запроса.
$resources = query(modResource::class)
->where(['published' => 1])
->debug()
->fetchAll();После выполнения метода fetchAll() будет выведена подробная информация:
=== pbQuery Debug ===
CACHE: 0
SQL: SELECT ... FROM ...
Queries: 1
Query Time: 0.003210 s
PHP Memory: 4.21 MB
Peak Memory: 4.67 MB
PHP Time: 0.015826 s
======================🔹 Что показывает debug
CACHE— результат получен из кеша (1 — да, 0 — нет).SQL— сгенерированный SQL-запрос.Queries— сколько SQL-запросов было выполнено.Query Time— общее время выполнения SQL-запросов.PHP Memory— объём текущего использования памяти.Peak Memory— пик использования памяти во время выполнения.PHP Time— общее время выполнения скрипта.
Метод debug() также использует getSQL(), чтобы отобразить итоговый SQL-запрос. Вы можете получить его отдельно через getSQL() — см. раздел Получение SQL-запроса.
💥 Быстрый вывод и остановка — dump()
Если нужно быстро посмотреть отладочную информацию и сразу завершить выполнение скрипта — используйте dump():
query(modResource::class)
->where(['template' => 2])
->dump();Выведет ту же самую отладочную информацию, что debug(), но сразу остановит выполнение скрипта.
🧠 Полезно при:
- Оптимизации запросов.
- Диагностике кеширования.
- Анализе потребления ресурсов.
- Поиске узких мест при работе с большими выборками.
📄 Получение SQL-запроса
Иногда полезно получить сгенерированный SQL-запрос в виде строки — для логирования, ручной проверки или отладки. Для этого используйте метод getSQL().
🔹 getSQL(): string
Возвращает SQL-строку, сгенерированную на основе текущего состояния запроса.
$sql = query(modResource::class)
->select('id,pagetitle')
->where(['published' => 1, 'parent' => 0])
->getSQL();
return $sql;SQL:
SELECT `modResource`.`id`, `modResource`.`pagetitle`
FROM `modx_site_content` AS `modResource`
WHERE `modResource`.`published` = 1 AND `modResource`.`parent` = 0