Пользовательские запросы к БД в MODx Revolution

в 21:14, , рубрики: modx, modx revolution, xpdo, запросы к БД, метки: , ,

Данный топик наверняка будет полезен тем, у кого довольно большие проекты на MODx Revolution, так как с обычными сайтами-визитками достаточно и стандартных методов работы а-ля $modx->getObject(), $modx->getCollection() и т.п. И данные методы по сути своей не просто работа с базой данных, а еще и с объектами MODx.

При работе с большими проектами методы а-ля $modx->getCollection() для нас не лучшее решение по двум причинам:
1. Перерасход ресурсов. Данные методы не просто получают данные из БД, но еще и создают инстанции получаемых объектов. В данном случае получая информацию о 10000 документов, мы получаем 10000 объектов modResource, что не очень круто.
2. Осложняется задача подсчета получаемых записей. Помимо прямых сложностей подсчета еще на уровне запроса, даже если вы получите 10 записей одного и того же документа (к примеру), MODx вернет вам как результат только один объект modResource. И хотя часто такое устроит многих программистов (они получили уникальные объекты и рады), кого-то это не устроит, так как опять же происходит перерасход ресурсов, а по конечному результату сразу и не видно, что запрос не оптимизированный.
К тому же при работе на крупных проектах нам чаще всего нужны не сами объекты, а только информация (записи из базы данных).

Описанные здесь методы работы с БД ставят 2 задачи:
1. Дать бОльшую гибкость в написании запросов к БД.
2. Придерживаться стандартных методов xPDO, то есть избежать чистого SQL, так как чистый SQL по некоторым причинам в фреймворках вообще не кашерно (хотя бы с точки зрения возможной миграции на другой тип БД, смены названий таблиц, префиксов или еще чего-нибудь)

Итак, к делу.

Для начала нам необходимо освоить важный метод

$modx->newQuery($class);

Для построения всех запросов в MODx всегда нужен хотя бы один базовый класс, от которого будет плясать весь запрос.

Вот более развернутый пример:

$q = $modx->newQuery('modResource');
$q->where(array(
	'context_key' => 'web'
));
$result = $modx->getCollection('modResource', $q);

В данном случае $q — это часто встречающаяся нам в документации так называемая criteria.
Это почти что тоже самое, что и where, когда мы передаем его в качестве второго параметра, только более мощный инструмент, так как у него много важных методов типа Sortby, leftJoin, innerJoin, Limit и другие.

Сейчас мы как раз получили то, с чем и собрались бороться, то есть на выходе мы получили несколько объектов modResource. Просто от этого привычного примера нам легче будет двинуться дальше к нашей цели.

Итак, несколько переделаем наш запрос.

$q = $modx->newQuery('modResource');
$q->where(array(
	'context_key' => 'web'
));
$q->prepare();
$sql = $q->toSQL();

Вот здесь мы уже получим чистый SQL, что наверняка много кому понадобится.
В данном примере мы увидели еще один важный метод

$q->prepare();

Он как раз и готовит конечный SQL.

Теперь же мы можем выполнить этот SQL

$q = $modx->newQuery('modResource');
$q->where(array(
	'context_key' => 'web'
));

$q->limit(10);    // Добавим лимит записей

$q->prepare();
$sql = $q->toSQL();

$query = $modx->prepare($sql);
$query->execute();

$result = $query->fetchAll(PDO::FETCH_ASSOC);
print_r($result);

На выходе мы как раз и получим массив данных.
Но колонки будут иметь не совсем удачные названия а-ля
[modResource_id] => 0
[modResource_type] => document

Чтобы было понятней, добавим явный SELECT в запрос.

$q = $modx->newQuery('modResource');
$q->where(array(
	'context_key' => 'web'
));
$q->select(array(
   'modResource.*'
));
$q->limit(10); 

$q->prepare();
$sql = $q->toSQL();

$query = $modx->prepare($sql);
$query->execute();

$result = $query->fetchAll(PDO::FETCH_ASSOC);
print_r($result);

Вот теперь все хорошо с именами колонок :-)

А теперь довольно полезный и наглядный пример: Получим 1/10 записей со сдвигом 1/20 и упорядочим по ID.

$q = $modx->newQuery('modResource');
$q->where(array(
	'context_key' => 'web'
));
$q->select(array(
   'modResource.*'
));

// Подсчитываем общее число записей
$total = $modx->getCount('modResource', $q);

// Устанавливаем лимит 1/10 от общего количества записей
// со сдвигом 1/20 (offset)

$q->limit($total / 10, $total / 20);   

// И сортируем по ID в обратном порядке
$q->sortby('id', 'DESC');

$q->prepare();
$sql = $q->toSQL();

$query = $modx->prepare($sql);
$query->execute();

$result = $query->fetchAll(PDO::FETCH_ASSOC);
print_r($result);

Кстати, довольно легко эти примеры переделать так, чтобы получить конечные объекты.


$q = $modx->newQuery('modResource');
$q->where(array(
    'context_key' => 'web'
));
$q->select(array(
   'modResource.*'
));

// Подсчитываем общее число записей
$total = $modx->getCount('modResource', $q);

// Устанавливаем лимит 1/10 от общего количества записей
// со сдвигом 1/20 (offset)

$q->limit($total / 10, $total / 20);   

// И сортируем по ID в обратном порядке
$q->sortby('id', 'DESC');

$q->prepare();

// Получаем объекты
$docs = $modx->getCollection('modResource', $q);

Суть методов $modx->getObject() и $modx->getCollection() заключается в том, чтобы получив данные из БД, инициировать указанный класс и набить в него полученные данные методом $object->fromArray($array());

Те, кому этот метод понравится, наверняка построят таким образом и более сложные запросы сразу из нескольких таблиц и т.п.

Автор: Fi1osof

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js