MODX (xPDO) - Получение объектов
Статья, в которой рассмотрим, что такое xPDO и как его использовать при написании сниппетов и плагинов для системы CMS MODX Revolution. Разберём основные методы xPDO, применяемые для получения объектов (getObject, getCollection, getIterator, getOne и getMany) и метод newQuery, который используется для составления запроса. Кроме этого рассмотрим способ получения данных из базы CMS MODX Revolution без создания объектов, т.е. с помощью PDO.
Что такое xPDO
xPDO (eXtension PDO) - это объектно-реляционный мост (ORB), положенный в основу системы CMS MODX Revolution. xPDO представляет собой библиотеку, построенную на основе PDO. Основная цель этой библиотеки – это предоставить разработчикам объектно-ориентированный программный интерфейс (API), который позволит более просто и эффективно создавать сложные веб-приложения. В основу xPDO положены объектные модели для различных платформ баз данных. Представляет из себя объектная модель xPDO обычный XML-файл. Например, для СУБД MySQL, данный файл называется modx.mysql.schema.xml
. Найти его в системе CMS MODX Revolution можно по следующему пути: /core/model/schema/
. Если данный файл открыть, то можно увидеть, что он состоит из объектов (классов). Каждый объект (класс) представляет собой некоторую таблицу, а свойства этого объекта – его столбцы.
Объектов (таблиц) в MODX Revolution достаточно много, но наиболее часто используются при написании снипетов и плагинов следующие: modResource
(ресурсы), modChunk
(чанки), modUser
(пользователи), modUserProfile
(профиль пользователя), modTemplateVarResource
(значение дополнительных полей), modTemplateVar
(дополнительные поля).

Примечание: в скобках на вышеприведённой схеме указаны названия таблиц без префикса. Каждая таблица представляет собой в xPDO модели тот или иной объект.
Как уже было отмечено выше, каждый объект (класс) имеет свойства (поля). Свойство - это столбец таблицы. Например, объект modResource
(ресурс) состоит из свойств id
(первичный ключ ресурса), pagetitle
(заголовок), description
(описание), content
(содержимое ресурса), publishedby
(id пользователя, который опубликовал ресурс) и т.д.
Кроме этого, каждый объект может иметь связи (alias) с другими объектами. Некоторые из них приведены на схеме. Например, если вам нужно получить имя пользователя, который опубликовал ресурс, то необходимо воспользоваться отношением, имеющим alias PublishedBy
. Данное отношение определяет то, как данные представленные объектом modResource связаны с данными объекта modUser посредством локального (publishedby) и внешнего ключа (id). Каждое отношение имеет мощность. Например, у отношения с псевдонимом PublishedBy
мощность равна 1. Это означает то, что отношение PublishedBy
связвает объект modResource
только с одним объектом modUser
.
Методы xPDO для получения объектов
В xPDO получение объектов осуществляются в основном с помощью следующих методов: getObject
, getCollection
, getIterator
.
Метод getObject
предназначен для получения одного объекта. Объект - это экземпляр класса xPDOObject
. Представить себе данный объект можно как строку в таблице базы данных.
Метод getCollection
предназначен для получения коллекции объектов. Коллекция - это массив объектов xPDOObject
. Представить себе данную коллекцию можно как список строк в таблице.
Метод Iterator
предназначен для получения коллекции специального вида. Эта коллекция отличается от getCollection
тем, что она не предоставляет доступ сразу ко всем её элементам (объектам xPDOObject
), обращение к ним осуществляется последовательно.
Метод getObject
getObject
- это метод, который позволяет получить объект, удовлетворяющий указанному критерию.
$modx->getObject($className, $criteria, $cacheFlag) // $className - имя объекта // $criteria (не обязательный) - критерий // $cacheFlag (не обязательный) - флаг, отвечающий за кэширование (по умолчанию true)
Метод getObject может принимать 3 аргумента: $className
, $criteria
, и $cacheFlag
. Первый аргумент - это имя класса (название объекта), который вы хотите получить. Второй аргумент - это критерий, на основании которого будет осуществляться поиск элемента. Третий (последний) аргумент определяет, необходимо ли кэшировать полученный объект.
Если последний аргумент ($cacheFlag
) имеет целое значение, то он будет определять время, на которое объект необходимо поместить в кэш. Если же аргумент $cacheFlag
имеет значение false
, то он не будет помещён в кэш. А если аргумент $cacheFlag
равен true
, то полученный объект будет помещён в кэш на неопределенный срок.
Аргумент $criteria
может принимать одно из 3 значений:
- значение первичного ключа;
- массив, содержащий одно или несколько условий;
- объект
xPDOCriteria
или его производные.
Например, напишем сниппет, который будет возвращать название (заголовок) ресурса, который имеет id = 5
:
// получим ресурс (в качестве критерия используем id ресурса) $resource = $modx->getObject('modResource',5); // если ресурс не найден, то тогда вернём соответствующий ответ if ($resource == null) { return 'Ресурс с id = 5 не найден!'; } // получим значения поля pagetitle у ресурса и вернём его return $resource->get('pagetitle');
Если метод getObject не может получить объект, удовлетворяющий указанному критерию, то он возвращает в качестве ответа значение null
.
Указывать критерии для поиска нужного объекта можно также посредством массива:
// получим ресурс (в качестве критерия используем массив) $resource = $modx->getObject('modResource',array('id'=>5));
Вы также можете осуществить поиск указанного объекта (например, ресурса) по нескольким полям:
// получим ресурс, который имеет статус опубликованного (published==1) и расположен в ресурсе с id = 19 // 1. Составляем критерий: $criteria = array( 'published'=>1, 'parent'=>19 ); // 2. Получаем объект, указав критерий $resource = $modx->getObject('modResource',$criteria); // 3. Если объект найден, то возвращаем его заголовок if ($resource) { return $resource->get('pagetitle'); } //4. Если объект не найден, то соответственного ничего возвращаем return;
Если указанному критерию соответствуют несколько объектов, то метод getObject
вернёт только первый из них.
Вы также можете создавать более сложные критерии для выбора элементов, используя xPDO запрос:
// новый запрос для объекта modResource $query = $modx->newQuery('modResource'); // условие запроса $query->where( array('parent' => 19) ); // получим объект, используя в качестве параметра запрос $resource = $modx->getObject('modResource', $query); // переменная для хранения результата $output = ''; // если ресурс найден, то... if ($resource) { // получим у найденного ресурса заголовок $output = $resource->get('pagetitle'); } return $output;
Метод getCollection
getCollection
- это метод, который возвращает коллекцию (массив) найденных объектов xPDOObjects
, удовлетворяющих указанному условию.
$modx->getCollection($className, $criteria, $cacheFlag) // $className - имя объекта // $criteria (не обязательный) - критерий // $cacheFlag (не обязательный) - флаг, отвечающий за кэширование (по умолчанию true)
Метод getCollection
принимает те же три аргумента, что и метод getObject
.
Например, создадим сниппет, который будет выводить заголовки всех ресурсов, который опубликовал пользователь с id = 1
:
// получить коллекцию объектов modResource, удовлетворяющих указанному критерию $resourses = $modx->getCollection('modResource',array( 'publishedby' => 7 )); // переменная, в которую будем сохранять результат $output = ''; // осуществим перебор массива объектов foreach ($resourses as $resourse) { // добавим к значению переменной поле pagetitle ресурса $output .= $resourse->get('pagetitle').'<br>'; } return $output;
Ключом массива, который возвращает метод getCollection
, выступает первичный ключ полученных объектов.
Попробуем, получим объект из коллекции, полученной методом getCollection
по его id
:
// получим массив пользователей $users = $modx->getCollection('modUser'); // получим имя пользователя из коллекции по id (например, 1) $id = 1; if ($users[$id]) { return $users[$id]->get('username'); } return 'Пользователя с id = '.$id.' не существует!';
Например, выведем имена всех пользователей CMS MODX Revolution:
// получить массив пользователей $users = $modx->getCollection('modUser'); // выведим информацию о количестве пользователей $output = 'Всего пользователей: '.count($users).'<br>'; // переберём массив пользователей foreach ($users as $user) { // добавить к значению $output имя пользователя $output .= $user->get('username').'<br>'; } // вернуть список пользователей return $output;
Вторым параметром может быть не только массив, но и объект xPDOQuery
:
// Например, получим 5 ресурсов, у которых id больше 10 // 1. Создадим запрос на выборку объектов modResource, у которых id > 10 $query = $modx->newQuery('modResource', array('id:>' => 10)); // 2. Ограничим выбору 5 элементами $query->limit(5); // 3. Получим объекты, указав запрос в качестве 2 параметра $resources = $modx->getCollection('modResource', $query); // 4. Переменная, в которую будем записывать результат $output = ''; // 5. Переберём с помощью foreach полученный массив foreach ($resources as $resource) { // 6. Поместим в переменную $output заголовок ресурса $output .= $resource->get('pagetitle').'<br>'; } // 7. Вернём результат (значение переменной $output) return $output;
Метод getIterator
xPDO метод getIterator
идентичен методу getCollection
за исключением того, что он позволяет обратиться в определённый момент времени только к одному объекту xPDOObject
из коллекции строк. С точки зрения использования памяти метод getIterator
является более эффективным, чем метод getCollection
и поэтому лучше использовать именно его. Исключение составляет только те моменты, когда вам необходимо иметь доступ сразу ко всем объектам (строкам) коллекции.
Например, получим ресурсы, опубликованные пользователем с именем "admin" и имеющие заголовок, в котором присутствует слово "Ubuntu":
// 1. Создаём новый запрос для объекта modResource $query = $modx->newQuery('modResource'); // 2. Присоединяем к объекту modResource объект modUser с помощью связи "PublishedBy" $query->leftJoin('modUser','PublishedBy'); // 3. Задаём условие $query->where(array( 'modResource.pagetitle:LIKE' => '%Ubuntu%', 'PublishedBy.username' => 'admin', )); // 4. Получаем коллекцию объектов с помощью метода getIterator, отвечающих запросу $resources = $modx->getIterator('modResource',$query); // 5. Создаём переменную, в которую будем записывать ответ $output = ''; // 6. Перебираем массив объектов foreach ($resources as $resource) { $output .= $resource->get('pagetitle').'<br>'; } // 7. Возвращаем ответ return $output;
Внимание: При итерации в качестве индекса объекта выступает не первичный ключ.
Метод get
Метод get
предназначен для того, чтобы получить значение указанного свойства (поля) объекта xPDOObject (строки таблицы).
/* синтаксис */ get($k) // $k - название поля (массив полей) // Например, получим значение поля pagetitle у объекта $object $pagetitle = $object->get('pagetitle'); // Например, получим значения полей id и pagetitle у объекта $object $row = $object->get(array('id','pagetitle')); $id = $row['id']; $pagetitle = $row['pagetitle'];
Метод newQuery
newQuery
– это метод, который позволяет строить сложные SQL запросы в объектно-ориентированном виде. Создание запроса осуществляется всегда относительно какого-то класса, указанного в качестве первого параметра. Другими словами, метод newQuery
создаёт новый xPDOQuery
для указанного класса xPDOObject
. Составленные запросы, например, можно передать в качестве аргумента $criteria
в метод getObject
или getCollection
.
Функция newQuery
создает объект xPDOQuery
. Она принимает 3 параметра:
$modx->newQuery($class, $criteria, $cacheFlag) // $class - имя класса, для которого необходимо создать запрос. // $criteria (необязательный)- используется для указания критерия. // $cacheFlag - подобно аргументу $cacheFlag метода getObject (позволяет указать, необходимо ли кэшировать этот запрос).
Для конструирования запроса можно использовать следующие методы:
- xPDOQuery.leftJoin - добавляет в запрос предложение LEFT JOIN. Операция LEFT JOIN выполняет внешнее левое соединение 2 таблиц так, что в результирующий набор попадут не только те соединения строк, которые удовлетворяют условию on, но и все остальные строки из первой (левой) таблицы. При этом отсутствующие значения столбцов из второй (правой) таблицы будут заменены значениями
NULL
./* синтаксис leftJoin */ // leftJoin ($class, $alias, $conditions) // $class - название класса (объекта) из модели xPDO // $alias (не обязательный аргумент) - название связи // $conditions (не обязательный аргумент) - условие соединения таблиц (если используете название связи из модели xPDO, то условие можно не указывать) // Например, выберем все ресурсы и имена пользователей, которые их создали. $query = $modx->newQuery('modResource'); $query->select(array( 'modResource.id as id', 'modResource.pagetitle as pagetitle', 'CreatedBy.username as username' )); // присоединяем объект modUser c помощью связи CreatedBy // (соединение связи не указываем, т.к. она описана в модели xPDO) $query->leftJoin('modUser','CreatedBy'); $resources = $modx->getCollection('modResource',$query); $output = ''; foreach ($resources as $resource) { $output .= $resource->get('username').' : '.$resource->get('pagetitle').'<br>'; } return $output;
- xPDOQuery.innerJoin - добавляет предложение INNER JOIN в запрос. Операция INNER JOIN выполняет внутреннее соединение 2 таблиц так, что в результирующий набор попадают только те соединения строк таблиц, которые отвечают условию on.
/* синтаксис innerJoin */ // innerJoin ($class, $alias, $conditions) // $class - название класса (объекта) из модели xPDO // $alias (не обязательный аргумент) - название связи // $conditions (не обязательный аргумент) - условие соединения таблиц (если используете название связи из модели xPDO, то условие можно не указывать) // Например, получим ресурсы, опубликованные пользователем "marry": $query = $modx->newQuery('modResource'); // 1 параметр - имя класса (объекта) // 2 параметр - название связи // 3 параметр - условие соединения (для связей, описанных в модели xPDO можно не указывать) $query->innerJoin('modUser','modUser','modUser.id=modResource.publishedby'); $query->where(array('modUser' => 'marry')); $resources = $modx->getCollection('modResource',$query);
- xPDOQuery.rightJoin - добавляет в запрос предложение RIGHT JOIN. Соединение RIGHT JOIN (правое внешнее соединение) обратно соединению LEFT JOIN. Это означает то, что в результирующий набор из второй (правой) таблицы попадут все строки. А из первой (левой) таблицы только те, для которых будет выполняться условие соединения on.
/* синтаксис rightJoin */ // rightJoin ($class, $alias, $conditions) // $class - название класса (объекта) из модели xPDO // $alias (не обязательный аргумент) - название связи // $conditions (не обязательный аргумент) - условие соединения таблиц (если используете название связи из модели xPDO, то условие можно не указывать) // Перепишем пример приведённый для метода leftJoin с помощью метода rightJoin $query = $modx->newQuery('modUser'); $query->select(array( 'modUser.id as id', 'modUser.username as username', 'CreatedResources.pagetitle as pagetitle' )); // присоединяем с помощью RIGHT JOIN таблицу (объект modResource) c помощью связи CreatedResources $query->rightJoin('modResource','CreatedResources'); $resources = $modx->getCollection('modUser',$query); $output = ''; foreach ($resources as $resource) { $output .= $resource->get('username').' : '.$resource->get('pagetitle').'<br>'; } return $output;
- xPDOQuery.where - добавляет в запрос предложение WHERE.
$query = $modx->newQuery('modResource'); /* синтаксис where array('attribute:operator' => 'value') */ // по умолчанию все условия в массиве соединяются между собой посредством AND $query->where(array( 'name' => 'Тимофей', /* равно (по умолчанию) */ 'name:=' => 'Тимофей', /* равно */ 'name:!=' => 'Тимур', /* не равно */ 'age:>' => '26', /* больше */ 'age:>=' => '26', /* больше или равно */ 'age:<' => '26', /* меньше чем */ 'age:<=' => '26', /* меньше или равно */ 'search:LIKE' => '%сайт%', /* инструкция LIKE */ 'field' => null, /* проверка на NULL */ 'ids:IN' => array(1,2,3), /* инструкция IN */ )); /* LIKE (условие, выбирающее записи, у которых значение поля attribute соответствует указанной маске value). Для создания маски можно использовать специальные символы % (любое количество символов) и _ (один символ). */ /* NOT LIKE (исключает из выборки записи, у которых значение поля attribute соответствует указанной маске value). $query->where(array('pagetitle:NOT LIKE' => '%сайт%')). */ /* IN (условие, выбирающие записи, у которых значения поля совпадает со значением в списке). */ /* NOT IN (условие, выбирающее записи, у которых значение поля не совпадает ни с одним значением в списке). $query->where(array('parent:IN' => array(15,20))); */ /* Как правило, элементы массива в предложении where соединяются с помощью SQL оператора "AND". Но вы также можете соединить условия и с помощью оператора "OR". Для этого его необходимо указать его в качестве префикса к необходимым именам столбцов. */ // Например, создадим условие, которое будет выбирать только те ресурсы, у которых в заголовке встречается фраза MODX или HTML $query->where(array( 'pagetitle:LIKE' => '%MODX%', 'OR:pagetitle:LIKE' => '%HTML%' )); /* Обратите внимание на то, что необходимо обязательно указывать операнд =, если вы указываете в условии ключ, содержащий подобную строку 'OR:disabled:=' => true. */
- xPDOQuery.andCondition - добавляет условие AND в предложение WHERE. Метод
andCondition
может быть использован только после методаwhere
.// Например, получим ресурсы, отвечающие одновременно 2 условиям: // - имеющие шаблон с id = 6 // - у которых родительский ресурс имеет id = 19 $query = $modx->newQuery('modResource'); $query->where(array('parent' => 19)); $query->andCondition(array('template' => 6)); $resources = $modx->getCollection('modResource',$query);
- xPDOQuery.orCondition - добавляет условие OR в предложение WHERE. Метод
orCondition
может быть использован только после методаwhere
.// Например, получим ресурсы, отвечающие одному из условий: // - имеющие шаблон с id = 6 // - у которых в качестве родителя выступает ресурс с id = 19 $query = $modx->newQuery('modResource'); $query->where(array('parent' => 19)); $query->orCondition(array('template' => 6)); $resources = $modx->getCollection('modResource',$query);
- xPDOQuery.limit - добавляет в запрос предложение LIMIT/OFFSET.
/* limit ($limit, $offset) */ // $limit - число, ограничивающее количество записей // $offset (не обязательный аргумент) - число, задающее смещение (т.е. какое количество записей необходимо пропустить сначала) // Например, пропустим первые 5 записей и выведем позиции с 6 по 10: $query = $modx->newQuery('modResource'); $query->limit(5,5); $resources = $modx->getCollection('modResource',$query);
- xPDOQuery.sortby - добавляет в запрос предложение ORDER BY.
/* синтаксис sortby */ // sortby($column, $direction) // $column - имя столбца // $direction (необязательный аргумент) - порядок сортировки (по умолчанию 'ASC') // направление сортировки (например, 'ASC' (в порядке возрастания), 'DESC' (в порядке убывания), 'RAND()' (в случайном порядке) и др. // Например, получить всех пользователей отсортированных по полю username: $query = $modx->newQuery('modUser'); $query->sortby('username','ASC'); $users = $modx->getCollection('modUser',$query); // Вы также можете отсортировать записи в случайном порядке с помощью функции RAND(): $query = $modx->newQuery('modResource'); $query->sortby('RAND()'); $resources = $modx->getCollection('modResource',$query); // Если вы хотите отсортировать результат в определённом порядке, то можете использовать функцию FIELD(): $query = $modx->newQuery('modResource'); $query->sortby('FIELD(id,1,7,3,4)'); $query->where(array('id:IN' => array(1,7,3,4))); $resources = $modx->getCollection('modResource',$query);
- xPDOQuery.select - указывает столбцы, которые необходимо вернуть из SQL запроса. Функция
select
работает как с массивом столбцов, так и со строкой (имена столбцов в строке указываются через запятую)./* синтаксис select */ select ($columns) // $columns - строка (по умолчанию *) или массив // Например, получить коллекцию объектов modUser, содержащих только поля id и username. $query = $modx->newQuery('modUser'); $query->select('id,username'); $users = $modx->getCollection('modUser',$query); // Кроме этого в функцию select можно передать имена столбцов с помощью метода getSelectColumns. // getSelectColumns($className, $tableAlias, $columnPrefix, $columns) // $className - имя класса (объекта) в модели xPDO // $tableAlias (не обязательный) - псевдоним (по умолчанию '') // $columnPrefix (не обязательный) - префикс для столбцов (по умолчанию '') // $columns (не обязательный) - массив, содержащий имена столбцов // С использованием метода getSelectColumns: $query->select($modx->getSelectColumns('modUser','modUser','',array('id','username')));
- xPDOQuery.groupby - добавляет в запрос предложение GROUP BY. Предложение GROUP BY обычно используется в сочетании с агрегатными функциями COUNT, MIN, MAX, AVG и SUM.
// Например, выведем количество ресурсов, опубликованных каждым пользователем: $query = $modx->newQuery('modUser'); $query->leftJoin('modResource','PublishedResources'); $query->groupby('PublishedResources.publishedby'); $query->select(array( 'COUNT(PublishedResources.id) as count', 'modUser.id as id', 'modUser.username as username' )); $users = $modx->getCollection('modUser',$query); $output = ''; foreach ($users as $user) { $output .= 'Пользователь '.$user->get('username').' опубликовал '.$user->get('count').' ресурсов <br>'; } return $output;
- xPDOQuery.setClassAlias - устанавливает SQL псевдоним для таблицы, представляющий собой основной класс (объект).
/* синтаксис setClassAlias */ // setClassAlias ($alias) // $alias (не обязательный аргумент) - устанавливает псевдоним классу (по умолчанию равен '') // Получим все объекты modResource, удовлетворяющие некоторому условию. // Для удобства чтения кода установим классу modResource псевдоним "Resource". $query = $modx->newQuery('modResource'); $query->setClassAlias('Resource'); $query->where(array('Resource.pagetitle:LIKE' => '%MODX%',)); $resources = $modx->getCollection('modResource',$query);
Например, создадим сниппет, который будет выводить на экран ресурсы и связанное с каждым из них значение TV поля с id = 3
.
Требования к написанию выборки:
- обязательное наличие TV поля, у которого
id = 3
; - родительский ресурс должен иметь
id
, равное 19, 24 или 25; - сортировку ресурсов выполнить по дате публикации.
$query = $modx->newQuery('modResource'); $query->select(array( 'modResource.id as id', 'modResource.pagetitle as pagetitle', 'TemplateVarResources.value as value' )); $query->innerJoin('modTemplateVarResource','TemplateVarResources'); $query->where(array( 'TemplateVarResources.tmplvarid:=' => '3', 'parent:IN' => array(19,24,25) )); $query->sortby('modResource.publishedon','ASC'); $query->limit(5); $resources = $modx->getCollection('modResource',$query); $output = ''; foreach ($resources as $resource) { $output .= $resource->get('pagetitle').'('.$resource->get('value').')<br>'; } return $output;
Отладка
Увидеть запрос, который был создан методом newQuery, можно так:
$query = $modx->newQuery('modResource'); // ... составление запроса // подготовка запроса к выполнению (в качестве результата получаем объект, связанный с этим запросом $query->prepare(); // возвращение подготовенного SQL запроса return $query->toSQL(); // Например, SQL запрос вышеприведённого примера будет иметь вид: /* SELECT modResource.id as id, modResource.pagetitle as pagetitle, TemplateVarResources.value as value FROM `modx_site_content` AS `modResource` JOIN `modx_site_tmplvar_contentvalues` `TemplateVarResources` ON `modResource`.`id` = `TemplateVarResources`.`contentid` WHERE ( `TemplateVarResources`.`tmplvarid` = '3' AND `modResource`.`parent` IN (19,24,25) ) ORDER BY modResource.publishedon ASC LIMIT 5 */
Методы getOne и getMany
Для работы со связанными объектами в xPDO используются методы getOne
и getMany
(в зависимости от мощности отношения).
getOne – это метод, который получает объект, связанный с текущим отношением, имеющим мощность 1:? (1:0 или 1:1) или 1:1.
getOne($alias, $criteria, $cacheFlag) // $alias - псевдоним отношения // $criteria (не обязательный) - критерий (объект) // $cacheFlag (не обязательный) - флаг, отвечающий за кэширование (по умолчанию true)
Например, рассмотрим, как получить объект modUser
, связанный с объектом modResource
отношением "PublishedBy", имеющим мощность 1.
// 1. Получим ресурс с id = 5 $resource = $modx->getObject('modResource',5); // 2. Присоединим к ресурсу объект modUser связанный с ним отношением "PublishedBy" $user = $resource->getOne('PublishedBy'); // 3. Получаем имя пользователя, опубликовавшего этот ресурс $username = $user->get('username'); // 4. Возвращаем ответ return $username;
getMany – это метод, который позволяет получить коллекцию объектов, связанных с текущим отношением 1:* (один ко многим).
getMany($alias, $criteria, $cacheFlag) // $alias - псевдоним отношения // $criteria (не обязательный) - критерий (объект) // $cacheFlag (не обязательный) - флаг, отвечающий за кэширование (по умолчанию true)
Например, разберём, как получить все ресурсы, созданные некоторым пользователем:
// 1. Получим пользователя по имени admin $user = $modx->getObject('modUser',array('username' => 'admin')); // 2. Получим ресурсы, созданные пользователем admin $resources = $user->getMany('CreatedResources'); // 3. Создадим переменную $output $output = ''; // 4. Переберём все ресурсы, созданные пользователем admin foreach ($resources as $resource) { $output .= $resource->get('pagetitle').'<br>'; } // 5. Возвратим ответ return $output;
Например, получим все чанки расположенные в некоторой категории:
// 1. Получим категорию с именем Tickets $category = $modx->getObject('modCategory',array('category' => 'Tickets')); // 2. Получим чанки, находящиеся в этой категории $chunks = $category->getMany('Chunks'); // 3. Создадим переменную $output $output = ''; // 4. Переберём чанки foreach ($chunks as $chunk) { // получим имя чанка $output .= $chunk->get('name').'<br>'; } // 5. Возвратим ответ return $output;
Вывод данных посредством PDO
Получить данные из базы MODX Revolution можно не только с помощью методов xPDO (getOject
, getCollection
и др.), но и посредством PDO в виде массива. Данный способ не является стандартным способом получения данных в CMS MODX Revolution, но зато он позволяет сэкономить память и увеличить быстродействие. Эти преимущества в основном проявляют себя только в том случае, если вам необходимо из базы получить большой объём информации.
Внимание: Способ получения данных, основанный на PDO, выполняет это без проверки прав, и конечно без создания объектов xPDOObject.
Например, получим названия последних 5 опубликованных ресурсов:
// 1. Конструирование запроса выполним с помощью xPDO (метод newQuery) $query = $modx->newQuery('modResource'); $query->sortby('publishedon','DESC'); $query->limit(5); $query->where(array('published:='=>'1')); // 2. Выполнение запроса выполним с помощью PDO // подготовка запроса к выполнению $query->prepare(); // выполнить подготовленный запрос $query->stmt->execute(); // получаем массив, содержащий все строки результирующего набора $rows = $query->stmt->fetchAll(PDO::FETCH_ASSOC); $output = ''; foreach ($rows as $row) { $output .= $row['modResource_pagetitle'].' (дата публикации: '.$row['modResource_publishedon'].')<br>'; } return output;
Комментарии: 46
А как получить массив ресурсов по заранее известному списку алиасов? Так не получается…
$resourses = $modx->getCollection('modResource',array(
'alias' => 'res1,res2'
));
- Александр Мальцев2021-10-06 15:12:47Привет!
$resourses = $modx->getCollection('modResource', [ 'alias:IN' => ['search', 'authorization'] ]);
- Amsterdam2021-10-06 20:14:40Спасибо!
Не подскажите, как в xPDO использовать регулярки? Например получили pagetitle
$pagetitle = $modx->resource->get('pagetitle'); return $pagetitle;результат: «Товар номер один, интернет-магазин Сталевар»
А как использовать регулярку в данном контексте? что то никак не могу придумать… Допустим, нужно удалить слово «Сталевар»
По аналогии с php не получается
$pagetitle = $modx->resource->get('pagetitle'); $pagetitle = preg_replace('Сталевар', '', $pagetitle); return $pagetitle;
- Александр Мальцев2021-06-20 14:32:19Привет!
Вместо preg_replace используйте функцию с поддержкой многобайтовых кодировок mb_ereg_replace:
$pagetitle = $modx->resource->get('pagetitle'); $pagetitle = mb_ereg_replace('Сталевар', '', $pagetitle); return $pagetitle;
Не подскажите, почему так происходит? Плагин пересчитывает цену в minishop2 через свой плагин. Имеется такой код:
switch ($modx->event->name) { case 'msOnGetProductPrice': // Подключаем массив значений по ссылке $values = & $modx->event->returnedValues; // Цена может меняться несколькими плагинами сразу, поэтому проверяем: if (isset($values['price'])) { $price = $values['price']; } // Меняем цену: $values['price'] = $_REQUEST['price']; $modx->log(modX::LOG_LEVEL_ERROR, $modx->event->name . '. Цена2 ' . $values['price'] . print_r($values, true)); break; }Цена должна пересчитываться посредством сложения и записи новых значений в value для input.price[name=price] на фронте, через выбор нужных радиокнопок. По задумке, начальное значение и новое должны складываться. Но, плагин сразу пересчитывает базовую цену и делает ее равной нулю.
Сложение происходит, но не с указанной ценой товара, а с нулем. Не могу разобраться, почему так…
- Александр Мальцев2020-10-18 14:47:38Привет! Так в $_REQUEST['price'] будет находиться строка, а нужно наверно число.
Попробуйте так:
$values['price'] = floatval($_REQUEST['price']);
- Amsterdam2020-10-20 08:38:50не выходит… все равно начальную цену выводит как 0
itchief.ru/assets/uploadify/e/6/9/e69a599b92bb1b49371d6f7d64b83636.png
Но при выборе модификации, цена меняется, но только на выбранную, то есть становится либо 500 либо 830. А должна суммироваться 500 + 500, либо соответственно, 500 + 830 - Amsterdam2020-10-20 08:56:09Вообще, похоже логику использую неверную. Пересчет цены должен происходить только при выборе модификатора, а она сразу же при загрузке страницы меняется, и меняется на ноль.
- Александр Мальцев2020-10-20 15:06:44Да, у вас это скорее всего должно происходить посредством AJAX, если конечно нужно выполнять какую-то обработку на сервере, или просто на клиенте. А так = получается, что при загрузке страницы данный параметр (price) не передаётся и у вас всегда цена устанавливается равной нулю.
Голову сломал над одной задачкой.
Есть код:
$pdo = $modx->getService('pdoFetch'); $collection = $pdo->getCollection('modResource', array('template' => 5, 'published' => 1), array( 'parents' => 28, 'sortbyTV' => 'event_date_time', 'includeTVs' => 'event_date_time,event_place', 'sortbyTVType' => 'datetime', 'sortdirTV' => 'ASC', 'where' => '{"event_place":"Место"}' ) ); foreach ($collection as $key) { // То-сё, пятое, десятое; }Не могу понять, возможно ли использовать строку
'where' => '{"event_place":"Место"}'в данной конструкции.
Подозреваю, что пишу её неправильно. Подставлял строку
'tvPrefix' => 'tv.',и на выходе вообще ничего, хмм…
- Александр Мальцев2020-09-16 14:13:48Привет! Условие where необходимо задавать во втором аргументе:
$collection = $pdo->getCollection('modResource', array('template' => 5, 'published' => 1, 'event_place' => 'Место'), array( 'parents' => 28, 'sortbyTV' => 'event_date_time', 'includeTVs' => 'event_date_time,event_place', 'sortbyTVType' => 'datetime', 'sortdirTV' => 'ASC', ) );
&where=`{"[[!pubday]]:=":"[[!today]]"}`В сниппете [[!today]] выводится «сегодняшняя» дата формата 2020-07-10, но никак не получается вывести в таком же формате дату публикации ресурса. Вывод publishedon напрямую вставляет туда и время (часы минуты секунды)
&where=`{"publishedon:=":"[[!today]]"}`изза чего сравнение, конечно, не проходит. Как возможно вывести дату публикации в виде «2020-07-10»?
- Александр Мальцев2020-07-11 13:45:51Например, это можно выполнить, если выбирать ресурсы, дата публикации которых находится между «2020-07-10 00:00:00» и «2020-07-10 23:59:59». При этом нужно учитывать, что publishedon в базе данных хранится в количестве миллисекунд (временной метке Unix).
Порядок действий:
1. Создать сниппет setDatePeriod, который будет устанавливать плейсхолдеры «date.start» и «date.end». Они будут хранить сегодняшнюю дату в миллисекундах на начало и конец дня.
<?php // получаем текущую дату $date_ts = time(); // начальная дата $day_start = strtotime('today', $date_ts); // конечная дата $day_end = strtotime('tomorrow', $day_start) - 1; return $modx->setPlaceholders(array( 'start' => $day_start, 'end' => $day_end, ),'date.');
2. Вызываем на странице сниппет setDatePeriod, который будет устанавливать плейсхолдеры «date.start» и «date.end».
[[!setDatePeriod]]
3. В pdoResources в &where прописываем условия:
[[pdoResources? ... &where = `{ "publishedon:>=": "[[+date.start]]", "publishedon:<=": "[[+date.end]]" }` ]]
- Amsterdam2020-07-11 17:53:45Вот это да, решение оказалось несколько более сложным, чем я думал. Троекратное спасибо, Александр!!! Вашу помощь нельзя переоценить!
- Aleksei2020-07-13 11:33:19Добрый день!
Осмелюсь предложить еще вариант, вроде как работает))
&where = `{ "publishedon:>=": "[[!+nowdate:default=`now`:strtotime:date=`%Y-%m-%d 00:00:00`]]", "publishedon:<=": "[[!+nowdate:default=`now`:strtotime:date=`%Y-%m-%d 23:59:59`]]" }`
- Amsterdam2020-12-06 09:37:36Александр, добрый день!
Не подскажите, а как сделать, что и вчерашние даты захватывались? Пробовал прописать и yesterday вместо tomorrow, и — 2, но не получается… - Александр Мальцев2020-12-07 08:41:29Привет! Нужно так:
// начальная дата $day_start = strtotime('yesterday', $date_ts); // конечная дата $day_end = strtotime('tomorrow', $day_start) - 1;

Сниппет:
if(is_numeric($_REQUEST['id'])) $id = $_REQUEST['id']; $user = $modx->getObject('modUserProfile', $id); if(!$user) return; return $modx->getChunk($tpl, array ( 'email'=> $user->get('email'), 'fullname'=> $user->get('fullname'), 'phone'=> $user->get('phone'), 'comment'=> $user->get('comment') ));Простейший чанк вывода:
<p>Имя пользователя: [[+fullname]]</p> <p>Email: [[+email]]</p> <p>Телефон: [[+phone]]</p> <p>Комментарий: [[+comment]]</p>Вывод на странице:
<form action=""> <input type="text" name="id" class="search" placeholder="Введите номер" /> <input type="submit" class="search" value="Найти" /> </form> [[!snippet? &tpl=`chank` ]]
- Александр Мальцев2019-11-24 15:35:21Привет!
В форме полю желательно установить соответствующее имя, например phone:
<input type="text" name="phone" class="search" placeholder="Введите номер" />
При выборе объекта нужно задавать условие по phone, а не id. В большинстве случаев телефон это не просто цифры. Поэтому проверить телефон с помощью is_numeric не всегда корректно. Если в поле phone кроме цифр есть знак +, скобочки и тире, то в этом случае следует удалять всё другое что ввел пользователь, крое этого и цифр.
$phone = preg_replace('/[^+()0-9-]/', '', $_REQUEST['phone']); if (empty($phone)) { return; } $user = $modx->getObject('modUserProfile',array('phone' => $phone)); if($user) { return $modx->getChunk($tpl, array ( 'email'=> $user->get('email'), 'fullname'=> $user->get('fullname'), 'phone'=> $user->get('phone'), 'comment'=> $user->get('comment') )); }
- Nik2019-11-25 07:32:08Спасибо! Насчет скобок и тире я не подумал, is_numeric проверяет же только цифры в строке. Еще вы использовали функцию empty. Все просто… То что я выдумывал целый день, у вас на это ушла одна секунда)))
Не поможешь разобраться с такой задачкой?
Написал код, который работает в паре с PHP парсером. Необходимая логика его работы следующая:
— Парсер передает ему массив данных (ок)
— Процессор MODx создает ряд ресурсов из этих данных (ок)
— Если ресурс найден, обновляем данные (ок)
— Если часть ресурсов найдена, а часть еще не была загружена (такое бывает, когда в каталоге появляются новые товары),
то здесь возникает проблема — старые ресурсы он обновляет, а вот новые не создает (пытается их обновить, но не находит).
Нужно доработать то, что бы не найденные ресурсы он создавал, а не упирался в попытку поиска для обновления. Код следующий:
<?php // массив с данными $data = [ 'pagetitle' => $main_heading, 'alias' => "$url", 'content' => '', 'template' => 3, 'published' => 1, 'parent' => $parent_id ]; // Заполняем ТВ $specForPriceTV = 'tv15'; $brandTV = 'tv14'; $data[$priceTV] = $price_result; $data[$brandTV] = $brand; // Процессор выполнение $response = $modx->runProcessor('resource/create', $data); // если ошибка if($response->isError()){ // echo "<div><b style='color:olive'>Документ не создан:</b> $main_heading</div>". $response->getMessage(); // Выполняем поиск по alias $resource = $modx->getObject('modResource', array('alias' => "$url")); // Если ресурс найден if (is_object($resource)) { // Устанавливаем заголовок $resource->set('pagetitle', "$main_heading"); // Обновляем TV $resource->setTVValue('price', $price_result); $resource->setTVValue('brand', $brand); // Сохранение $resource->save(); echo "<div><b style='color:olive'>Документ обновлен:</b> $main_heading</div>"; } else { echo "<div><b style='color:red'>Документ не найден:</b> $main_heading</div>"; } } else{ echo "<div><b style='color:green'>Документ создан:</b> $main_heading</div>"; }
- Amsterdam2019-08-12 07:50:58нет идей?.. а если за финансовую помощь?)
- Александр Мальцев2019-08-12 15:44:51Привет!
Сначала лучше проверить, если ли уже такой ресурс. Если да, то обновить его, в противном случае создать.
// массив с данными $data = [ ... ]; // выполняем поиск ресурса по alias $resource = $modx->getObject('modResource', array('alias' => "$url")); // если ресурс найден if (is_object($resource)) { // то обновляем ему поля и затем его сохраняем } else { // в противном случае создаём новый }
Полный код:
// массив с данными $data = [ 'pagetitle' => $main_heading, 'alias' => "$url", 'content' => '', 'template' => 3, 'published' => 1, 'parent' => $parent_id ]; // выполняем поиск ресурса по alias $resource = $modx->getObject('modResource', array('alias' => "$url")); // ели ресурс найден if (is_object($resource)) { // устанавливаем ему заголовок $resource->set('pagetitle', "$main_heading"); // обновляем TV $resource->setTVValue('tv14', 'Значение TV14'); $resource->setTVValue('tv15', 'Значение TV15'); // сохраняем $resource->save(); echo "<div><b style='color:olive'>Документ обновлен:</b> $main_heading</div>"; } else { // создаём ресурс через процессор $response = $modx->runProcessor('resource/create', $data); if ($response->isError()) { echo "<div><b style='color:red'>Ошибка при создании документа:</b> $main_heading</div>"; //echo $response->getMessage(); } $modx->cacheManager->clearCache(); $newId = $response->response['object']['id']; $newResource = $modx->getObject('modResource', $newId); $newResource->setTVValue('tv14', 'Новое значение TV14'); $newResource->setTVValue('tv15', 'Новое значение TV15'); $newResource->save(); echo "<div><b style='color:green'>Документ создан:</b> $main_heading</div>"; }
- Александр Мальцев2019-08-12 15:52:16Поблагодарить всегда можно на этой страничке.
- Amsterdam2019-09-10 08:44:26Доброго дня! Только удалось вернуться к теме и протестировать. Спасибо за вариант, но не совсем работает. Когда идет попытка создать новый документ после тех, которые уже были и обновились, то происходит проблема с передачей значений TV. То есть:
Как говорил, процессор работает в паре с PHP парсером сайтов.
Было создано два ресурса. Процессор прошелся по ним обновив данные.
Третьего ресурса не было и он должен создаться, при этом он создается, но дальнейший цикл обхода ломается с ошибкой:
Fatal error: Uncaught Error: Call to a member function setTVValue() on null
Не могу понять, почему с получением данных на этапе создания ресурса происходит проблема.
Возможно, он попадает в цикл создания ресурсов и не выходит для обновления? - Александр Мальцев2019-09-10 15:35:10Привет! Должно работать, пример тестировал. Может какая-та проблема с MODX. Попробуйте обновить CMS MODX на туже версию, которая стоит.
- Amsterdam2019-09-10 23:22:37Не совсем понял, на какую предлагается обновить? Версия самая свежая, 2.7.1…
- Amsterdam2019-09-10 23:29:28Заметил: если убрать обращение к TV, то ресурс создается. Т.е. вот так:
// массив с данными $data = [ 'pagetitle' => $main_heading, 'alias' => "$url", 'content' => '', 'template' => 3, 'published' => 1, 'parent' => $parent_id ]; // выполняем поиск ресурса по alias $resource = $modx->getObject('modResource', array('alias' => "$url")); // ели ресурс найден if (is_object($resource)) { // устанавливаем ему заголовок $resource->set('pagetitle', "$main_heading"); // обновляем TV $resource->setTVValue('tv14', 'Значение TV14'); $resource->setTVValue('tv15', 'Значение TV15'); // сохраняем $resource->save(); echo "<div><b style='color:olive'>Документ обновлен:</b> $main_heading</div>"; } else { // создаём ресурс через процессор $response = $modx->runProcessor('resource/create', $data); if ($response->isError()) { echo "<div><b style='color:red'>Ошибка при создании документа:</b> $main_heading</div>"; //echo $response->getMessage(); } $modx->cacheManager->clearCache(); $newId = $response->response['object']['id']; $newResource = $modx->getObject('modResource', $newId); //Тут были TV $newResource->save(); echo "<div><b style='color:green'>Документ создан:</b> $main_heading</div>"; }
Но пока не понял, обновляет ли он данные при таком раскладе - Александр Мальцев2019-09-11 07:09:28Попробуй просто переустановить эту же версию, если стоит 2.7.1, то 2.7.1.
- Amsterdam2020-07-08 20:44:58Приветствую, Александр!
В массив передаю значения:
$data = [ 'pagetitle' => $main_heading, 'alias' => "$url", 'content' => '', 'template' => 3, 'published' => 1, 'parent' => $parent_id ];
но не могу понять, как через этот массив записать данные в какой либо TV? - Amsterdam2020-07-08 22:13:20В общем задача та же самая, что и в коде выше по нашей переписке, только сравнение нужно провести не по значению в alias, а по значению из TV (имя ТВ siteUrl)
- Александр Мальцев2020-07-09 16:17:55Чтобы найти ресурс по значению TV нужно использовать запрос:
$criteria = $modx->newQuery('modResource'); $criteria->leftJoin('modTemplateVarResource', 'TV', 'TV.tmplvarid = 5 AND TV.contentid = modResource.id'); $criteria->where([ 'TV.value' => 'tabs' ]); $resource = $modx->getObject('modResource',$criteria); if (is_object($resource)) { // ... }
В примере вместо 5 вам нужно указать id вашей TV, а значение по которому найти вместо tabs. - Amsterdam2020-07-09 19:03:17Александр спасибо, шикарно работает!!!
- Amsterdam2020-07-09 21:53:02А нет, не работает, оказывается) Call to a member function setTVValue() on null. Что то я опять не так делаю… Опробовал этот код и с массивом, и без массива, различные ошибки выдает…
Не подскажешь, как найти всех детей ресурса с id=3, у которых значение tv1 = 'fraza', и удалить эти ресурсы? И так и так попробовал, не получается и все тут…
- Александр Мальцев2019-08-05 12:34:51Привет.
Создай в корне проекта php-файл со следующим содержимым и запусти его на выполнение:
<?php require_once dirname(__FILE__).'/config.core.php'; require_once MODX_CORE_PATH.'model/modx/modx.class.php'; $modx = new modX(); $modx->initialize('web'); $modx->getService('error','error.modError', '', ''); $query = $modx->newQuery('modResource'); $query->leftJoin('modTemplateVarResource', 'TemplateVarResources'); $query->where(array( 'parent:=' => '3', 'TemplateVarResources.tmplvarid:=' => '2' )); $resources = $modx->getCollection('modResource', $query); foreach ($resources as $resource) { $id = $resource->get('id'); $res = $modx->getObject('modResource', $id); // пометить на удаление $res->set('deleted', '1'); $res->save(); // или полностью удалить //$res->remove(); } exit();
Только вместо 2, укажи номер своего id tv1 = 'fraza' - Amsterdam2019-08-05 15:07:25Спасибо!!!
Наконец-то всё по полочкам.
Я туточки по баннерам покликаю… Надеюсь никто не против?
:-))
$q = $modx->newQuery('modContext'); $where = array( 'modContext.key:in' => array('web', 'mgr'), 'cs.value:!=' => NULL, 'cs.value:!=' => '', ); $q->select(array( 'modContext.key', 'cs.key as setting_key', 'cs.value' )); $q->innerJoin('modContextSetting', 'cs', 'cs.context_key = modContext.key'); $q->where($where);здесь where присваивается за один раз. Как бы разбить это действие на несколько? Типа:
$where['modContext.key:in'] => 'web'; $where['modContext.key:in'] => 'mgr'; $where['cs.value:!='] => NULL; $where['cs.value:!='] => '';Помогите, пожалуйста.
Вообще, мне нужно сделать так, чтобы where собирался по ходу кода, а потом использовался в двух разных запросах. Для этого нужно, чтобы в where элементы добавлялись вместе с условиями «like», «in» и т.п. У меня получалось только, что они перекрывали друг друга, где в итоге только последний элемент в массиве оставался.
- Александр Мальцев2018-11-08 13:39:01Собирайте, это обычная работа с массивами на php.
$where['modContext.key:in'][0] = 'web'; $where['modContext.key:in'][1] = 'mgr'; $where['cs.value:!='] = NULL;
- Руслан2018-11-08 13:42:32Спасибо большое за помощь. Уже докумекал. => вместо = писал. Туплю иногда)
В этом коде:
$criteria = $modx->newQuery('geodata'); $criteria->select($modx->getSelectColumns('geodata')); $criteria->where("MATCH(city_name) AGAINST ('*:query*' IN BOOLEAN MODE)"); $criteria->sortby('country_name', 'DESC'); $criteria->prepare(); $str="Моск"; $criteria->stmt->execute(array('query'=>$str)); $collection = $modx->getCollection('geodata', $criteria);и в этом параметры не устанавливаются и, как следствие, результат = 0;
$str='Моск'; $sql = " SELECT * FROM modx_geodata WHERE MATCH(city_name) AGAINST ('*?*' IN BOOLEAN MODE) "; $statement = $modx->prepare($sql); $statement->bindValue(1, $str, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT); $result = $statement->execute(); print_r($statement->rowCount());Спасибо.
- rumano2017-04-25 20:55:50Решил просто заэкранировать:
$sQuery = 'Мос'; // $sQuery = $modx->stripTags($sQuery); $sQuery = $modx->sanitizeString($sQuery); $criteria = $modx->newQuery('geodata'); $criteria->select($modx->getSelectColumns('geodata')); $criteria->where("MATCH(city_name) AGAINST ('*{$sQuery}*' IN BOOLEAN MODE)"); $criteria->sortby('country_name', 'DESC'); // $criteria->prepare(); //$criteria->stmt->execute(); $collection = $modx->getCollection('geodata', $criteria, false); foreach($collection as $k => $v) { print_r($v->get('city_name') ."\n"); }
Пробую написать сниппет такого плана.
<?php
$options = $modx->getIterator('msOption');
$output = array();
$onlyoptions = marka, model;
foreach($options as $onlyoptions ){
$output[] = $onlyoptions ->get('key').'=='.$option->get('value');
}
return $output;