Наверх

Часть 4. Связи объектов, getOne, getMany, работа с ТВ

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

Итак, создайте новый TV-параметр, назначьте его одному из шаблонов и у какого-нибудь ресурса установите значение этого параметра.

Думаю, вы уже догадались, что TV-параметры — это тоже объекты) У TV-параметров класс modTemplateVarResource.

Давайте посмотрим, как же выглядят эти объекты:
$tvs = $modx->getCollection('modTemplateVarResource');
foreach ($tvs as $k => $tv) {
    $tvs[$k] = $tv->toArray();
}
print '<pre>';
print_r($tvs);
Если хоть у одного из ресурсов заполнено хоть одно из дополнительных полей, вы увидите это значение в результате выполнения скрипта.

У объектов класса modTemplateVarResource есть поля: id, tmplvarid, contentid и value.
tmplvarid — id самого дополнительного поля (во вкладке «Элементы» рядом с названием TV),
contentid — id ресурса,
value — соответственно, значение данного TV-параметра у данного ресурса.
Соответственно, чтобы узнать значение TV у какого-то ресурса надо найти объект modTemplateVarResource, у которого значение contentid соответствует id ресурса, а tmplvarid — id нужного нам TV:
$where = array(
        'contentid' => 16
      , 'tmplvarid' => 4
    );
$tv = $modx->getObject('modTemplateVarResource', $where);
return $tv->get('value');

Мы с вами познакомились со связью между объектами: в данных одного объекта указаны параметры другого (в объекте modTemplateVarResource есть поле, в котором указано значение поля id объекта modResource). И это не просто так. Объекты modResource и modTemplateVarResource связаны друг с другом. О типах связей мы поговорим в другой раз, я сейчас покажу, как можно использовать связи, чтобы упростить наш код.

У объектов xPDO есть методы getOne и getMany
getOne() — метод, который возвращает связанный объект
getMany() — метод, который возвращает несколько связанных объектов
Давайте, попробуем получить TV-параметр используя его связь с документом:
$res = $modx->getObject('modResource', 16);
$tvs = $res->getMany('TemplateVarResources');
foreach ($tvs as $k => $tv) {
    $tvs[$k] = $tv->toArray();
}
print '<pre>';
print_r($tvs);
Этот код выведет нам значения всех TV-параметров указанного документа. Посмотрите внимательно на параметр, который передается в метод getMany(). Это не название класса (мы знаем, что у TV класс modTemplateVarResource). Это псевдоним или, другими словами, алиас связи. Давайте посмотрим, как выглядит для MODX связь между ресурсом и TV-параметрами:
[composites]
    [TemplateVarResources]
        [class] => modTemplateVarResource
        [local] => id
        [foreign] => contentid
        [cardinality] => many
        [owner] => local
composites — в этом массиве перечислены жесткие связи (помимо TV там есть и другие связи). Это означает, что при удалении ресурса удаляются и все связанные объекты.
TemplateVarResources — это тот самый псевдоним связи.
class — это класс связанного объекта. Вот здесь мы видим, что указан класс modTemplateVarResource.
local, foreign — здесь указано, по какому полю связаны объекты. В local указывается, какое поле нашего объекта используется в связи, а в foreign указывается, какое поле в связанном объекте соответствует указанному в local.
cardinality — может быть many или one. Если many, значит связь «Один — ко многим», а если one — «Один — к одному». В данном случае один — ко многим. То есть у каждого ресурса может быть много TV-параметров.
owner — указывает, какой объект в связи главный. То есть, если мы удалим ресурс, TV-параметры тоже удалятся, но если мы удалим TV, то ресурс останется.

MODX смотрит в этот массив связей каждый раз, когда выполняет getOne или getMany. Можно представить это так:
$res = $modx->getObject('modResource', 16);
$tvs = $res->getMany('TemplateVarResources');
    // ищем связь, у которой в алиасе указано TemplateVarResources
    // смотрим сначала class - о, класс modTemplateVarResource
    $class = 'modTemplateVarResource';
    // смотрим, по каким полям связаны объекты
    $local = 'id';
    $foreign = 'contentid';
    // формируем условие выборки - должен получиться такой массив:
    // $where = array('contentid' => $res->get('id'));
    $where = array($foreign => $res->get($local));
    // и возвращаем объекты (используем getCollection, так как
    // вызван метод getMany, а не getOne):
    $output = $modx-getCollection($class, $where);

Как вы поняли, объектов в MODX много и у каждого свои поля, кроме того многие объекты связаны друг с другом. MODX-то знает все эти объекты и связи. А как нам-то узнать, например, правильный алиас связи, который надо передать в метод getOne? Или, например, по каким полям связаны объекты? Или вообще, какие поля у объекта есть? Тут надо либо заучить, либо добавить себе в закладки эту страничку: bobsguides.com/modx-object-full-reference.html

Там вы найдете все объекты MODX, у объектов перечислены все поля, указано, в какой таблице хранятся данные объекта, и перечислены все связи объектов (пункты aggregates и composites).

Например, если посмотрим объект modUser, то увидим, что он связан с объектом modUserProfile и связь там один — к одному ([cardinality] => one), соответственно мы можем получить профиль пользователя без всяких дополнительных условий, так:
$user = $modx->getObject('modUser',1);
// Не забываем, что указывается АЛИАС СВЯЗИ, а не класс
$profile = $user->getOne('Profile');

Ну и напоследок хочу добавить, что у ресурсов (у объектов класса modResource) есть метод getTVValue, который выдает значение связанного TV-параметра:
$res = $modx->getObject('modResource', 16);
$tv = $res->getTVValue(4);
return $tv;

Но он может возвращать значение не только по id TV-параметра, но и по его названию:
$res = $modx->getObject('modResource', 16);
$tv = $res->getTVValue('image');
return $tv;

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

Задание 1. Написать сниппет, который будет выводить на страницу заголовки, аннотации и даты публикации только тех ресурсов, у которых значение TV-параметра «Выводить на главной» равно 1. Или если таких ресурсов больше 10 будет выводить сообщение «Найдено больше 10 ресурсов»
Задание 2. Напишите сниппет, который будет при каждом просмотре страницы увеличивать значение TV-параметра «Просмотры» на 1. А потом напишите второй сниппет, который выведет общее количество просмотров всех страниц, созданных пользователем. Подсказка — текущего пользователя можно получить так:
$user = $modx->user
и в переменной $user у нас будет объект класса modUser с полями текущего пользователя.
P. S. Я жду вопросов от новичков, потому что после такого материала вопросы возникнуть должны. Не бойтесь спрашивать, в этом топике вас даже за глупые вопросы никто не заминусует. Все учимся. По отсутствию вопросов создается впечатление, что никто эти топики не читает и никому это не надо — xPDO все прекрасно и так знают.

Решение задания для тех, кто не совсем понял тему или для тех, кому просто интересно.

Оригинал статьи: community.modx-cms.ru/blog/modx-xpdo/10268.html

89 комментариев

  1. Алексндр Наумов 18 апреля 2013, 17:14(Комментарий был изменён) # 0
    Илья, спасибо за серию статей про xPDO, все очень здорово разжевано. Не переживайте, вопросы обязательно будут, просто сначала хочется ознакомится со всеми постами вашего сайта.
    1. Denis Malafey 24 апреля 2013, 22:42(Комментарий был изменён) # 0
      Спасибо большое, Илья! Скажите, а почему многие не советую создавать не создавать TV параметры вообще? Говорили, что они сильно влияют на скорость. Это так? И если так, как без них обойтись вообще? Или как сделать так, чтоб они не влияли на скорость?
      Спасибо.
      1. Илья Уткин 25 апреля 2013, 00:19(Комментарий был изменён) # 0
        Так говорят, потому что каждое значение TV-параметра — это отдельный объект. Если вы показываете на странице 30 ресурсов, то это выборка 30 объектов. Но если у каждого ресурса есть еще по 5 TV-параметров (картинка, дата, теги, количество просмотров, количество лайков), то это уже 150 объектов, соответственно нагрузка в 5 раз больше.

        Если все сниппеты вызываются кешируемыми и изменения на сайте вносятся не очень часто (например, только раз в день), то никакой лишней нагрузки не будет. А для остальных случаев в любом случае придется писать что-то свое.

        Посмотрите на сайт fsknmsk.ru/ — там все сделано стандартно для MODX. У каждой новости могут быть заполнены ТВ-параметры: Картинка, большое изображение для слайдера на главной, галочка — показывать ли на главной, список фотографий для генерации галереи, ссылка на файл видео-записи, ссылка на аудио-запись, список прикрепленных файлов (MIGX). У страниц с обращениями вообще каждый пункт из этой формы — это отдельный TV. Но сайт работает довольно быстро, на обычном аккаунте shared-хостинга и не выдает даже 50% позволенной нагрузки.
        1. Denis Malafey 25 апреля 2013, 00:27(Комментарий был изменён) # 0
          Понял, спасибо!
          1. Denis Malafey 25 апреля 2013, 01:09(Комментарий был изменён) # 0
            Илья, а возможно ли использовать компонент Tickets только для комментариев?
            И как? Будет ли их модерация в админке?
            Все документы уже создал.

            Спасибо.
            1. Илья Уткин 25 апреля 2013, 11:43(Комментарий был изменён) # 0
              Можно. Просто устанавливаете Tickets и в шаблоне вызываете сниппет TicketsComments в том месте, где нужны комментарии. Насчет премодерации — здесь Василий пишет, что в новой версии премодерация есть. Единственное, комментарии могут оставлять только зарегистрированные пользователи.
              1. Denis Malafey 25 апреля 2013, 11:57(Комментарий был изменён) # 0
                Спасибо! Жаль, что только зарегистрированные пользователи могу оставлять. Поставлю решение Валентина, modxTalks.
        2. Виктор Якуша 25 апреля 2013, 03:34(Комментарий был изменён) # 0
          В первую очередь хочу выразить благодарность за Ваш ресурс. А теперь к делу…
          Как практическое задание решил написать такой сниппет:
          У меня есть TV (тип недвижимости, id=2) со значениями Дом||Квартира||…
          Хочу написать сниппет, который выводит все ресурсы у которого этот TV со значением — Дом. Вот код:
          <?php
          $criteria=array('id'=>2,'value'=>'Дом');
          $tvs=$modx->getCollection('modTemplateVarResource',$criteria);
          $output='';
          $output.='Всего найдено '.count($tvs).' записей.
          ';
          foreach ($tvs as $tv) {
              $resource=$tv->getOne('Resource');
              $output.='ID='.$resource->get('id').'. Название='.$resource->get('pagetitle');
          }
          return $output;
          
          Результат:
          Всего найдено 1 записей.
          ID=3. Название=Дом 1 // выводит не все, таких ресурсов 3 — Дом 1, Дом 2, Дом 3.
          Помогите решить.
          Спасибо!
          1. Илья Уткин 25 апреля 2013, 11:37(Комментарий был изменён) # 0
            Заходим сюда и в пункте fields смотрим, что у объекта modTemplateVarResource следующие поля: tmplvarid, contentid и value, а вы в массиве условий выборки указываете id (надо tmplvarid)
            1. Виктор Якуша 25 апреля 2013, 13:59(Комментарий был изменён) # 0
              Илья, спасибо Вам за быстрый ответ. Все получилось.
              P.S. жду новых статей по xPDO о ООП.
          2. Denis Malafey 26 апреля 2013, 21:27(Комментарий был изменён) # 0
            Здравствуйте, Илья.
            Мне нужно сделать похожие документы к ресурсу.
            Создал TV-параметр «same», он должен содержать id похожих ресурсов через запятую.
            Скажите, как вывести информацию о всех документах, id которых содержаться в TV-параметре?

            Пока есть такой код, но он выводит информацию только в том случае, если в значении TV-параметра содержится id только одного похожего документа.
            <?php
            
            $site_url = $modx->getOption('site_url');
            $id = $modx->resource->id;
            
            $res = $modx->getObject('modResource', $id);
            $id_res = $res->getTVValue('same');
            
            $q = $modx->newQuery('modResource');
            $q->where(array('id' => $id_res));
            $q->prepare();
            $q->stmt->execute();
            $recipe = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
            
            foreach($recipe as $res)
            {
                $output .= '<li><a href="'.$site_url.$res['modResource_uri'].'">';
                $output .= '<span>'.$res['modResource_menutitle'].'</span></a></li>';
            }
            return $output;
            Надеюсь, что внятно объяснил.
            Заранее спасибо.
            1. Илья Уткин 26 апреля 2013, 21:36(Комментарий был изменён) # 0
              Можно поменять 10 строчку так:
              $q->where(array('id:IN' => $id_res));
              Но лучше использовать сниппет getResources — будет меньше запросов и нагрузка будет поменьше, да и поддерживать такой код будет легче:
              [[getResources?
               &parents=`-1`
               &resources=`[[*same]]`
               &limit=`0`
               &tpl=`tpl.sameResources`
              ]]
              И чанк tpl.sameResources:
              <li><a href="[[~[[+id]]]]"><span>[[+menutitle]]</span></a></li>
              1. Denis Malafey 26 апреля 2013, 21:42(Комментарий был изменён) # 0
                Спасибо большое!
                Так мы наоборот отказываемся от всех сниппетов.
                Говорят, что getResources глючный и лучше писать свои сниппеты.
                А в связке с TV пишут, что getResources — это смерть, так как делает какие-то подзапросы, и производительность теряется в разы.
                Мы уже весь Wayfinder заменили своими сниппетами.
                Хотим сэкономить ресурсы и выиграть в скорости. Уже выиграли 0,3 сек.
                А вот без TV как вообще обойтись…
                1. Илья Уткин 26 апреля 2013, 22:13(Комментарий был изменён) # 0
                  Ой-ой-ой… Кто вам так голову заморочил? Wayfinder и getResources написали сами авторы MODX наиболее оптимальным образом. А чтобы выиграть в скорости, кешируйте все, что можно — целыми блоками.

                  И по вашему коду есть еще комментарий.
                  $id = $modx->resource->id;
                  
                  $res = $modx->getObject('modResource', $id);
                  $id_res = $res->getTVValue('same');
                  $modx->resource это уже объект (текущий ресурс) и можно было написать так:
                  $id_res = $modx->resource->getTVValue('same');
                  И кроме того, при использовании getResources будет всего один запрос (мы же не указываем &includeTVs=`1`).
                  1. Denis Malafey 26 апреля 2013, 23:07(Комментарий был изменён) # 0
                    Кешировать не получается ничего. Мультиязычность, Babel.
                    Я пробовал ваш метод, спасибо вам за него. Но, когда у меня всё кешируется — то не переводится :)
                    Я пробовал и решение Валентина
                    modx.im/blog/research/643.html
                    modx.im/blog/addons/663.html

                    Это просто супер, но не для меня. Мультиязычность.
                    Плюс ко всему стоит LikeDislike, HitsPage, modxTalks. То есть, некоторые блоки нельзя кешировать.

                    Я общался с пользователем Vanchelo (Ваня Брежнев, очень хороший паренёк) и читал мнения F1losofa по поводу getResources.
                    Они говорят, что нужно писать свои сниппеты.
                    Поверьте, это никак не моё мнение. Просто читаю, слушаю бывалых. Вас, конечно же, тоже слушаю.

                    Я поставил Wayfinder, getResources, и т.п. Скорость загрузки страницы отвратная. Бывает и 7 секунд простреливает. Всё кеширую, что можно. Уже напрягает. Отказался от Wayfinder — увидел, что реально выиграл в скорости. Надо пробовать.
                    А вот Сайт Вани (Vanchelo).
                    Посмотрите, летает же. А у него только свои сниппеты.
                    Или Thai, его тоже Ваня делал. Свои сниппеты. Вот это — класс.
                    Вот к таким сайтам, я считаю нужно стремится.

                    Ведь, посудите, в тех Wayfinder и getResources для простых сайтов дофига лишней фигни, которая просто не нужна. НАпример, для меню мне просто нужно вывести 5 пунктов ссылками. Всё. А там и параметры для исключения и кучу всего.
                    1. Илья Уткин 27 апреля 2013, 01:39(Комментарий был изменён) # 0
                      Для мультиязычности кеширование тоже можно применять. Надо добавить только язык к ключу файла кеша. Переделанный сниппет chunk для мультиязычности будет выглядеть так:
                      $cache_key = "chunk_".$name."_".$lang;
                      
                      $output = $modx->cacheManager->get($cache_key);
                      
                      if (empty($output)) {
                        $output = $modx->getChunk($name, $scriptProperties);
                        $modx->cacheManager->set($cache_key,$output);
                      }
                      
                      return $output;
                      (только сниппет вызывайте некешированным, чтобы $lang менялся) и тогда для каждого языка будет свой кеш. Если на сайте много блоков с одинаковой информацией на нескольких страницах, то выигрыш будет значительным. fsknmsk.ru/ тоже летает, но на нем нет нестандартных сниппетов — просто большой сайт-визитка.

                      Если вы заметили разницу в загрузке страниц после перехода на самописные сниппеты, тогда да, используйте их.

                      LikeDislike, HitsPage и ModxTalks ненагруженные сниппеты, они и разрабатывались, чтобы вызываться некешированными. Попробуйте те блоки, которые одинаковыы для всех страниц кешировать с добавлением языка к ключу и отпишитесь, поможет или нет)
                      1. Denis Malafey 27 апреля 2013, 03:08(Комментарий был изменён) # 0
                        Конечно, отпишу! Спасибо вам, Илья!
                        Ещё хотел узнать, а если свои сниппеты + ваш метод кеширования блоков, то вообще летать будет?
                        1. Denis Malafey 27 апреля 2013, 04:09(Комментарий был изменён) # 0
                          Не работает ваша модификация сниппета под мультиязычность.
                          Попробовал — всё как и было, не переключаются между языками. Только русский.
                          Пробовал и некешированным вызывать, и кешированным.
                          1. Илья Уткин 27 апреля 2013, 16:57(Комментарий был изменён) # 0
                            Блин, ну конечно. Я же не знаю, как у вас мультиязычность организована. Вы должны сами передать язык в переменную $lang
              2. Виктор Якуша 01 мая 2013, 04:53(Комментарий был изменён) # 0
                Здравствуйте, Илья.
                Подскажите, где ошибка…
                $criteria=array('tmplvarid'=>6);
                $tvs = $modx->getCollection('modTemplateVarResource',$criteria);
                $name_tv = $tvs->getOne('TemplateVar');
                $name = $name_tv->get('description');
                return $name;
                
                1. Илья Уткин 06 мая 2013, 16:50(Комментарий был изменён) # 0
                  Прошу прощения, что долго не отвечал — был далеко от компьютера на выходных)

                  Давайте посмотрим на вторую и третью строчки. Вы используете метод getCollection, который возвращает массив, соответственно к этому массиву нельзя применить метод getOne — он применяется только к объекту. Давайте попробуем пройтись по массиву и применить этот метод к первому попавшемуся объекту (написать $tvs[0] мы не можем, так как в массиве id элемента будет таким же, как и его id в базе данных):
                  $criteria=array('tmplvarid'=>6);
                  $tvs = $modx->getCollection('modTemplateVarResource',$criteria);
                  foreach ($tvs as $tv) {
                    $name_tv = $tv->getOne('TemplateVar');
                    $name = $name_tv->get('description');
                    if ($name) break;
                  }
                  return $name;
                  Получилось. Давайте смотреть дальше — зачем вы берете метод getCollection, если нужен только один объект — это только лишняя нагрузка? Поменяем getCollection на getObject:
                  $criteria=array('tmplvarid'=>6);
                  $tv = $modx->getObject('modTemplateVarResource',$criteria);
                  $name_tv = $tv->getOne('TemplateVar');
                  $name = $name_tv->get('description');
                  return $name;
                  Так, а зачем мы вообще идем от объекта modTemplateVarResource? А если наш ТВ еще не заполнен ни у одного ресурса — тогда мы не узнаем ничего об этом ТВ. Давайте работать сразу с объектом modTemplateVar:
                  $criteria=array('id'=>6);
                  $tv = $modx->getObject('modTemplateVar',$criteria);
                  $name = $tv->get('description');
                  return $name;
                  А так как мы производим поиск по id, то можно записать еще проще:
                  $tv = $modx->getObject('modTemplateVar',6);
                  return $tv->get('description');
                  1. Виктор Якуша 02 июня 2013, 19:57(Комментарий был изменён) # 0
                    Добрый день, Илья.
                    Спасибо Вам, за статьи и ответы.

                    Скажите, когда будут новые материалы, очень жду. newQuery(), select()…
                    1. Илья Уткин 14 июня 2013, 11:09(Комментарий был изменён) # 0
                      Про newQuery() и select() много материала как у bezumkin'а, так и у Fi1osof'а. У меня уроки для начинающих. Изучив их можно легко понять статьи по ссылкам. Думаю, я продолжу про xPDO, но нужно подыскать тему, которая еще не освещена в интернете.
                2. Владимир Гришин 01 июня 2013, 16:49(Комментарий был изменён) # 0
                  Илья привет. подскажи если я получаю значение тв через getObject('modTemplateVarResource', $where)
                  и результат является в таком виде: «name1:10;name2:11;name3:12»
                  как мне отсюда вытащить значение name1 равное 10?
                  1. Илья Уткин 14 июня 2013, 11:03(Комментарий был изменён) # +1
                    Сорри, что долго не отвечал. Я бы разбил строку на элементы массива и работал с ним:
                    $result = explode(";", "name1:10;name2:11;name3:12");
                    foreach ($result as $key => $val) {
                      $item = explode(":", $val);
                      $result[$item[0]] = $item[1];
                      unset($result[$key]);
                    }
                    В результате должен получиться такой массив $result:
                    array(
                        'name1' => '10'
                      , 'name2' => '11'
                      , 'name3' => '12'
                    )
                  2. Evgeny Epifanov 23 декабря 2013, 15:20(Комментарий был изменён) # 0
                    Илья, подскажите, пожалуйста, как можно высчитать md5 файла (tv-image)?
                    Если сделать так так:
                    $res = $modx->getObject('modResource', 309);
                    $tv = $res->getTVValue('image');
                    $md5 = md5($tv);
                    return $md5;
                    выводится md5 пути, а нужно именно файла.
                    1. Илья Уткин 23 декабря 2013, 15:23(Комментарий был изменён) # 0
                      Вам нужно использовать другую функцию.
                      1. Evgeny Epifanov 23 декабря 2013, 17:03(Комментарий был изменён) # 0
                        Спасибо большое!
                        $res = $modx->getObject('modResource', 309);
                        $tv = $modx->getOption('site_url').$res->getTVValue('image');
                        $md5 = md5_file($tv);
                        return $md5;
                        Отлично считает!
                        Только в «Console» кучу ошибок пишет:
                        Could not load class xpdofilecache from xpdofilecache
                        и
                        Language string not found:
                        Подозреваю, что это из-за настроек кэширования, так ли это?
                        1. Илья Уткин 23 декабря 2013, 17:09(Комментарий был изменён) # 0
                          Вот этого не знаю… Не сталкивался еще с таким… Если ошибки не пропадут попробуйте построчно останавливать скрипт и понять, на какой строке ошибка — может, вообще не этот скрипт ее генерирует.
                    2. Олег 09 июня 2014, 08:59 # 0
                      Илья, добрый день!
                      <?php
                      $criteria=array('tmplvarid'=>2,'value'=>'Дом');
                      $tvs=$modx->getCollection('modTemplateVarResource',$criteria);
                      $output='';
                      $output.='Всего найдено '.count($tvs).' записей.
                      ';
                      foreach ($tvs as $tv) {
                      $resource=$tv->getOne('Resource');
                      $output.='ID='.$resource->get('id').'. Название='.$resource->get('pagetitle');
                      }
                      return $output;

                      Приведенный выше код работает отлично в связке в GetRecources, но только по одному параметру TV, а как сделать выборку по двум и более параметрам? (чтобы между значениями Tv параметров стояло не ||, а AND )?
                      Заранее спасибо за помощь!
                      1. Илья Уткин 09 июня 2014, 09:28 # 0
                        Вам надо, чтобы скрипт нашел ресурсы, у которых и ТВ «house», и ТВ «home» равны строке 'Дом'? Я просто не совсем понимаю, что нужно-то?
                      2. Олег 09 июня 2014, 10:12 # 0
                        Добрый день еще раз!
                        Скажем у меня есть еще один tv ресурс с
                        'tmplvarid'=>5,'value'=>'здесь значение из формы'.
                        Мне нужно, чтобы у меня выдавало ресурсы, когда первый параметр = Дом и второй параметр = значению из формы. Вот как-то так. Надеюсь, смог объяснить. У меня идет выборка из формы вот такого вида:
                         <div class="checkbox-label">Страна</div>
                                                            <select name="country" class="country">
                                                                <option value="Испания" selected>Испания</option>
                                                                <option value='Италия'>Италия</option>
                                                                <option value='Чехия'>Чехия</option>
                                                                <option value='Польша'>Польша</option>
                                                                <option value='Россия'>Россия</option>
                                                                <option value='Китай'>Китай</option>
                                                            </select>
                        
                        Код, который обрабатывает, этот выбор, вот такой:
                        <?php
                        $criteria = array('tmplvarid'=>5,'value'=>$_POST['country']);
                        $templatepost = '{"template:=":3}';
                        $parent='13,21,27,50,51,64,14,52,53,54,15,55,56,16,17,57,58,59,60,61,62,63';
                        $criteriapost=array(
                            'elementClass' => 'modSnippet',
                            'element'      => 'getResources',
                            'showHidden'   =>'1',
                            'parents'      =>$parent,
                            'includeTVs'   => '1',
                            'processTVs'   => '1',
                            'limit'        => '100',
                            'tvPrefix'     =>  'tv.',
                            'tpl'          => 'LisRowTpl.poisk',
                            'tvFilters'    => '$criteria',
                            'where'        => '{"template:=":3}' 
                        );
                        
                            
                            $res=$modx->runSnippet('getResources', $criteriapost);
                            echo $res;
                        
                        Я не могу сообразить, как добавить второй параметр в массив, так чтобы шла проверка по обеим параметрам и если в случае успеха и в первом случае и во втором, то выводится объект.
                        1. Илья Уткин 09 июня 2014, 10:22 # 0
                          Здесь, в общем-то, работы с xPDO нет — вы просто задаете параметр tvFilters для getResources. Значит, надо смотреть в документацию: rtfm.modx.com/extras/revo/getresources
                          $criteria = 'hometv==Дом,country=='.$_POST['country'];
                        2. Олег 09 июня 2014, 11:16 # 0
                          Огромное спасибо за подсказку, Илья! Вы мне минимум день моей жизни сэкономили, я копал в другую сторону. )))
                          1. Андрей Иванов 08 июля 2014, 08:27 # 0
                            Илья, доброго времени суток.

                            А каким образом, например, сюда:

                            $where = array(
                                    'contentid' => 16
                                  , 'tmplvarid' => 4
                                );
                            можно передать условие AND? Ну, допустим, сравнить два параметра? Как передать «больше», «меньше» и т д?
                            1. Илья Уткин 08 июля 2014, 12:31 # 0
                              Здесь как раз и передано условие AND:
                              `contentid` = 16 AND `tmplvarid` = 4
                              Добавить условие OR можно так:
                              $where = array(
                                      'contentid' => 16
                                    , 'OR:tmplvarid' => 4
                                  );
                              Больше/меньше, LIKE:
                              $where = array(
                                      'contentid:>' => 16
                                    , 'tmplvarid:LIKE' => '%'.4.'%'
                                  );
                              Вот подсказочка: http://rtfm.modx.com/xpdo/2.x/class-reference/xpdoquery/xpdoquery.where
                              1. Андрей Иванов 09 июля 2014, 07:15 # 0
                                Илья, большое спасибо!
                            2. Мустафа 16 августа 2014, 15:31 # 0
                              Стоит задача добавить и(или) редактировать нахождение ресурса в группах.
                              Прошу подсказать реализацию.
                              1. Мустафа 16 августа 2014, 17:53(Комментарий был изменён) # 0
                                Благодарю, нашел решение покопавшись в манах. Может кому пригодится, примеры:
                                // Изменение группы ресурсов 
                                $docId='4';
                                $newGroup='2';
                                $resource = $modx->getObject('modResource', $docId);
                                $resGroups = $resource->getOne('ResourceGroupResources');
                                $resGroups->set('document_group', $newGroup);
                                $resGroups->save();
                                
                                //Получение списка групп ресурса
                                $docId='4';
                                $resource = $modx->getObject('modResource', $docId);
                                $resGroups = $resource->getMany('ResourceGroupResources');
                                $groups = array();
                                foreach ($resGroups as $group) {
                                  $groupObj = $group->getOne('ResourceGroup');
                                  $groups[] = $groupObj->get('name');
                                }
                                
                                //Добавление новой группы ресурсов
                                $group = $this->modx->newObject('modResourceGroup');
                                $groupdata = array('name'=>'NEW_GROUP_NAME','private_memgroup'=>0,'private_webgroup'=>0);
                                $group->fromArray($groupdata);
                                $group->save();
                                1. Evgeny Epifanov 09 февраля 2015, 20:56 # 0
                                  Илья, появилась такая задача:
                                  Найти все TV ресурса, которые находятся в определенной категории (сами TV находятся в категории, не ресурсы), а потом вывести их названия и значения (можно еще и порядок сортировки). В таблице все это есть (поля: category, caption, rank), а вот как все это вывести не знаю.
                                  Подскажи, пожалуйста.
                                  1. Илья Уткин 10 февраля 2015, 09:11 # 0
                                    $tvars = $modx->getCollection('modTemplateVar', array('category' => 14));
                                    $ids = array();
                                    foreach($tvars as $tvar) {
                                      $ids[] = $tvar->id;
                                    }
                                    $tvs = $modx->getCollection('modTemplateVarResource', array(
                                        'contentid' => $modx->resource->id,
                                        'tmplvarid:IN' => $ids
                                    ));
                                    foreach ($tvs as $k => $tv) {
                                        $tvs[$k] = $tv->toArray();
                                        $tvs[$k]['caption'] = $tvars[$tv['tmplvarid']]['caption'];
                                        $tvs[$k]['name'] = $tvars[$tv['tmplvarid']]['name'];
                                    }
                                    print_r($tvs);
                                    Но код не тестировал. Проверьте…
                                    1. Evgeny Epifanov 10 февраля 2015, 18:22 # 0
                                      Спасибо за ответ, Илья.
                                      Код к сожалению не работает. Выводит просто «Array ( )»
                                  2. MODX 14 сентября 2015, 11:55 # 0
                                    Такой вопрос:
                                    В сниппете А вызываю снипет Welcome и передаю ему параметры:

                                    $output = $modx->runSnippet('Welcome',array(
                                    'name' => 'John'
                                    ));

                                    Как в сниппете А принять параметр 'name' в переменную?
                                    1. Илья Уткин 14 сентября 2015, 11:57 # 0
                                      Этот параметр уже в переменной $name
                                      1. MODX 01 октября 2015, 07:56 # 0
                                        Благодарю, очень выручил.
                                    2. D.Zanzo 07 октября 2015, 21:10 # 0
                                      В описании getMany() опечатка:
                                      метод, который возвращает несколько связанных объектовДавайте, попробуем получить TV-параметр используя его связь с документом:
                                      1. Илья Уткин 08 октября 2015, 10:30 # 0
                                        Cпасибо, поправил
                                      2. Леви Ким 23 ноября 2015, 08:52 # 0
                                        подскажите, на сколько я читал, ТВ параметры очень сильно тормозят вывод всего документа?.. За счет чего это происходит? ведь это по сути дела всего один дополнительный запрос к базе.
                                        1. Илья Уткин 23 ноября 2015, 09:04 # 0
                                          Так они и не тормозят — именно потому что это всего один дополнительный запрос к базе.

                                          Раньше, когда все пользовались сниппетом getResources, проблема была — при большом количестве ТВ-шек вывод сильно подтормаживал. А теперь есть pdoResources — он выбирает все ТВ-параметры ресурса за один запрос.

                                          Конечно, 20-30 ТВ-параметров могут повлиять и на скорость pdoResources, но если настроить кеширование, то тормозить будет только первый раз.
                                          1. Леви Ким 02 декабря 2015, 15:44 # 0
                                            Скажи тогда пожалуйста, отчего же тогда я постояннь натыкаюсь вот на такие посты? с тв работает все долго, без тв — быстро
                                            modx.pro/solutions/7198-simplify-work-with-tv/#comment-50483
                                            1. Илья Уткин 02 декабря 2015, 17:32 # 0
                                              К каждому проекту свой подход. )

                                              Эта статья мне тоже очень понравилась. Решает не только вопрос скорости, но и другой минус ТВ-шек — то, что значения по умолчанию в базу данных не вносятся. В этом случае очень сложно сортировать и фильтровать по ТВ.
                                        2. Руслан 24 декабря 2015, 10:42 # 0
                                          Здравствуйте!
                                          В общем совсем я уже устал искать ответ на свой вопрос. Нигде нет описания как правильно через pdoResources вызывать тв поле с url
                                          Вот как вызываю я
                                          [[pdoResources? &parents=`5` &includeTVs=`newurl` &tpl=`new`]]
                                          Тв поле «newurl» с параметром ввода url и с параметром вывода url (ставил и параметр вывода -текст)
                                          В чанке «new» вызываю это поле так
                                          <a href="[­[+tv.newurl]]">ссылка</a>
                                          В результате имею вместо сторонней ссылки на внешний сайт ссылку вида
                                          мойсайт.ру/[­[+tv.newurl]]
                                          Илья, что я делаю не так?
                                          1. Илья Уткин 24 декабря 2015, 10:46 # 0
                                            По-моему, в pdoTools у ТВ по умолчанию нет префикса. Попробуйте так
                                            <a href="[­[+newurl]]">ссылка</a>
                                            1. Руслан 24 декабря 2015, 10:51 # 0
                                              Илья, спасибо за оперативность! Но так результат тот же.
                                              1. Руслан 24 декабря 2015, 19:08 # 0
                                                Всё разобрался. Опечатка где-то была.
                                            2. Сергей Карпук 02 января 2016, 11:23 # 0
                                              День добрый всем! Илья, огромное спасибо за данную серию статей!
                                              Вот решился несколько строк кода написать, вероятно, очень корявых. Надеюсь, дальше пойдёт веселее.
                                              Задание 1:
                                              $where = array ('tmplvarid' => 2);
                                              $tvs = $modx->getCollection('modTemplateVarResource',$where);
                                              
                                              foreach ($tvs as $k => $tv) {
                                                  $tvs[$k] = $tv->toArray();
                                                  if ($tvs[$k][value] == 1) {
                                                      $cont = $cont + 1;
                                                      $id = $tvs[$k][contentid];
                                                      $res = $modx->getObject('modResource',$id);
                                                      $output .= '<p><b>' .$res->get('pagetitle'). '.</b> ' .$res->get('introtext'). ' (' .$res->get('publishedon'). ')</p>';
                                                  }
                                              }
                                              
                                              if ($cont <= 10) {
                                                  return $output;    
                                              } else {
                                                  echo 'Найдено больше 10 документов';
                                              }
                                              1. Сергей Карпук 02 января 2016, 11:25(Комментарий был изменён) # 0
                                                Тоже задание, но уже с использованием метода getTVValue:
                                                $resource = $modx->getCollection('modResource');
                                                foreach ($resource as $k => $res) {
                                                    if (($res->getTVValue(2)) == 1) {
                                                        $cont = $cont + 1;
                                                        $output .= '<p><b>' .$res->get('pagetitle'). '.</b> ' .$res->get('introtext'). ' (' .$res->get('publishedon'). ')</p>';
                                                    }    
                                                }
                                                if ($cont <= 10) {
                                                    return $output;    
                                                } else {
                                                    echo 'Найдено больше 10 документов';
                                                }
                                                Буду очень благодарен за любые советы.
                                                1. Илья Уткин 09 января 2016, 14:25 # 0
                                                  Так вот же, комментарии как раз по первой задаче) ilyaut.ru/xpdo/xpdo-for-dummies-part-4-the-practice-of/
                                                  1. Сергей Карпук 09 января 2016, 14:34 # 0
                                                    Да, комментарии тоже прочитал. Но сначала пробовал решить самостоятельно.
                                              2. Станислав Однолетко 15 июля 2017, 02:39(Комментарий был изменён) # 0
                                                Чревато перегревом
                                                1 первое задание

                                                $i=0;
                                                $resources = $modx->getCollection('modResource',array( 'parent'=>'2'));
                                                $getTable = $modx->getCollection('modResource',array( 'parent'=>'2'));
                                                foreach($getTable as $key=>$gTab){
                                                    $val=$gTab->getTVValue('textTV');
                                                    if(!(empty($val))){
                                                         $i++;
                                                    }
                                                }
                                                    if($i>5){
                                                        echo "$i больше 5";
                                                    }else{
                                                        foreach($resources as $key=>$r){
                                                            $tv=$r->getTVValue('textTV');
                                                            if(!(empty($tv))){
                                                                print '<pre>';
                                                                 echo "id=".''.$r->get('id').' '.$r->get('pagetitle').' '.$r->get('introtext').' '.'Дата публикации:'.$r->get('publishedon').' '.'<b>Значение TV поля =</b>'.' '.$tv.'
                                                ';
                                                         }
                                                        } 
                                                    }
                                                1. Александр 05 октября 2017, 12:34(Комментарий был изменён) # 0
                                                  Илья, подскажите как получить список tv-полей из определенной категории?

                                                  То есть я в админке распределил поля (их довольно много) по категориям и хочу выводить в при разных условиях разные поля, по принципу «если id_ресурса=11, то показываем поля из id_категории=2».

                                                  Понимаю, что нужно обращаться через getCollection(), getMany() или getOne(), но не получается (
                                                  1. Илья Уткин 05 октября 2017, 13:02 # 0
                                                    Ну, готового решения у меня нет, с такой задачей я ещё не сталкивался. В общих чертах — да, нужно сначала получить нужный объект modCategory, потом с помощью getMany() получить связанные с ним объекты modTemplateVar, ну и наполнить значениями, получив для каждого ТВ соответствующие объекты modTemplateVarResource.

                                                    Вот здесь можно посмотреть, какие поля есть у того или иного объекта: Список объектов MODX
                                                  2. Константин 06 апреля 2018, 12:24 # 0
                                                    Илья подскажите пожалуйста, как дописать схему так чтобы объекты удалялись если удаляется modResource или msOption

                                                    Пробовал так, не удаляет
                                                    <?xml version="1.0" encoding="UTF-8"?>
                                                    <model package="sync_pack" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package="sync_pack"
                                                           version="1.1">
                                                    	<object class="SyncProducts" table="sync_products" extends="xPDOSimpleObject">
                                                    		....
                                                    		<field key="product_id" dbtype="int" attributes="unsigned" precision="10" phptype="integer" null="false" />
                                                    	
                                                            	<aggregate alias="ProductSync" class="modResource" local="product_id" foreign="id" cardinality="one" owner="foreign" />
                                                           	....
                                                    	</object>
                                                    </model>
                                                    1. Илья Уткин 21 мая 2018, 07:47 # 0
                                                      Чтобы объекты удалялись при удалении «родительского» объекта, связь должна быть композитной, а не агрегатной. Причём композитную связь нужно указывать именно у объекта modResource. Например, в файле metadata.mysql.php или вот таким плагином: https://modx.pro/solutions/7037/
                                                    2. Наталья 20 мая 2018, 13:55 # 0
                                                      Добрый день.
                                                      В виде таблицы нужно вывести tv-параметры ресурса, разбитые на категории. То есть в одном месте ресурса tv одной категории, в другом tv другой категории. В каждой категории tv-параметры еще будут добавляться, поэтому у каждого из них устанавливаю цифру в поле «Сортировка», чтобы в админке, в ресурсе, в дополнительных полях видеть их в нужном порядке (ну и в шаблоне, где tv привязаны — тоже установлена сортировка).

                                                      Пользуюсь вот таким сниппетом.
                                                      <?php
                                                      $q = $modx->newQuery('modTemplateVarResource'); //новый запрос
                                                      $q->select(array(
                                                         'modTemplateVarResource.value as value',
                                                         'tv.caption as name'
                                                      ));
                                                      $q->innerJoin('modTemplateVar', 'tv', 'tv.id = modTemplateVarResource.tmplvarid');
                                                      $q->where(array(
                                                          "modTemplateVarResource.contentid" => $modx->resource->get('id'),
                                                          "tv.category" => "13" // Выведуться только TV, которые без категории (можно удалить или заменить категорию)
                                                          ));
                                                      $q->limit(50);
                                                      $q->prepare();
                                                      $q->stmt->execute();
                                                      $result = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
                                                      $output = "";
                                                      foreach($result as $v){
                                                          $output .= $modx->getChunk($tpl,$v);
                                                      }
                                                      echo $output;
                                                      Естественно, он мне выводит в таблицу tv-параметры, отсортированные по tv.id

                                                      1 вопрос — как отсортировать их по значению в поле Сортировка tv-параметра
                                                      2 вопрос — как сделать, чтобы категорию задавать при вызове сниппета
                                                      "tv.category" => "13"
                                                      и может быть кто-то подскажет более простое решение…

                                                      изначально в шаблоне вызывала чанк в который передавала имя Tv и его значение. Но если разделов несколько и в них несколько tv-параметров, не хочется следить, все вывела через чанк, или что-то забыла.

                                                      Спасибо всем, кто откликнется.
                                                      1. Илья Уткин 21 мая 2018, 07:50 # 0
                                                        Допишите перед $q->limit(50); такую строку:
                                                        $q->sortby('tv.rank','ASC');
                                                        1. Василий 26 июня 2018, 12:30 # 0
                                                          Но он может возвращать значение не только по id TV-параметра, но и по его названию:
                                                          Заметил в последнее время уже на нескольких сайтах проблему с поиском значения TV по его названию, только по ID возвращает.
                                                          Это сказывается и на pdoTools, например следующий код не возвращает ничего:
                                                          {$_modx->resource.tv_name}
                                                          
                                                          Иногда (хотя не всегда) выручает феномовский модификатор:
                                                          {$_modx->resource|resource:'tv_name'}
                                                          

                                                          Например сейчас в Console следующий код возвращает пустоту:
                                                          $res = $modx->getObject('modResource', 40);
                                                          $tv = $res->getTVValue('site_settings');
                                                          print $tv;
                                                          
                                                          А через ID вернёт нужное мне значение:
                                                          $res = $modx->getObject('modResource', 40);
                                                          $tv = $res->getTVValue(27);
                                                          print $tv;
                                                          
                                                          Есть мысли отчего так происходит? И как лучше вывести значение TV, если есть только его название, а не id?
                                                          1. Amsterdam 12 июля 2018, 08:08(Комментарий был изменён) # 0
                                                            Товар 1==tovar1||Товар 2==tovar2||Товар 3==tovar3
                                                            Как во фронт вывести значение на кириллице, а не латынь? В админке выводит русские названия, а по вызову TV, на странице отображает «tovar1» и пр.

                                                            Подскажи пожалуйста?!
                                                            1. Илья Уткин 12 июля 2018, 10:03 # 0
                                                              Надо установить дополнение getTvDisplayName — оно как раз для этого и предназначено. Вот тут немного информации: ilyaut.ru/addons/interesting-addons-3/
                                                              1. Amsterdam 12 июля 2018, 12:52 # 0
                                                                Прекрасный плагин. После установки поломал раздел «установка пакетов», теперь дополнения не отображаются, и его не удалить)) А при выводе на странице выдает ошибку, т.е. не работает.
                                                                1. Илья Уткин 12 июля 2018, 12:57 # 0
                                                                  А в статье комментарии читали?
                                                                  1. Amsterdam 12 июля 2018, 13:05(Комментарий был изменён) # 0
                                                                    Был бы он еще в сниппетах, что бы знак вопроса убрать)) Его и в левой колонке нет, и список модулей теперь не открывается, что бы удалить. Хотя это произошло после установки.

                                                                    Теперь просьба подсказать, как его еще можно корректно вычленить и убрать из системы?)
                                                                    1. Илья Уткин 12 июля 2018, 13:08 # 0
                                                                      Можно прям в базе найти его в таблице modx_site_snippets. Или попробовать восстановить сайт из последнего бэкапа.
                                                                      1. Amsterdam 12 июля 2018, 13:23(Комментарий был изменён) # 0
                                                                        Был бы БэкаАп, Я бы не спрашивал) Бэкап делается раз в сутки, за сегодня было сделано уже много. Перед установкой каждого плагина ненабэкапишься) Да и не было такого никогда, что бы в MODx плагин ломал админку. Opencart — часто, но тут)
                                                                        1. Amsterdam 12 июля 2018, 14:04 # 0
                                                                          В общем нету его и в таблице. Был только в core/packages. Удаление оттуда физически конечно ничего не дало. Дело труба))
                                                                          1. Amsterdam 13 июля 2018, 13:26 # 0
                                                                            Рекомендую убрать этот плагин из рекомендованых. Это не плагин а убийца. даже после обновления системы проблема не решилась. В БД его нет, нигде нет, а пакеты больше не отображаются
                                                                            1. Илья Уткин 13 июля 2018, 13:49 # 0
                                                                              Не то чтобы я защищал автора этого дополнения, но мне стало интересно, я попробовал установить его на тестовом сайте. Да, вызов сниппета вызывает ошибку, но я спокойно открыл код сниппета в админке, удалил этот злополучный вопрос и всё заработало. Вот доступы, чтобы посмотреть, что всё работает:

                                                                              Адрес: http://s14720.h10.modhost.pro/manager/
                                                                              Логин: s14720
                                                                              Пароль: PEDAUw2xoGgq
                                                                              1. Amsterdam 13 июля 2018, 13:53 # 0
                                                                                А установил наверно, как все нормальные люди, через загрузчик? А я, скачал его с сайта, по ссылке что ты давал в теме. Вот черт меня дернул так сделать) Просто было в доступности клика, перешел по сслыке — скачал — загрузил пакет выбрав вручную из админки.

                                                                                Попробуй повторить, если не затруднит, должно так же глюкануть ;)
                                                                                1. Илья Уткин 13 июля 2018, 14:01 # 0
                                                                                  Ух-ты, действительно… Жесть.

                                                                                  Удали строчку из таблицы modx_transport_packages — должно заработать.
                                                                                  1. Amsterdam 13 июля 2018, 14:29 # 0
                                                                                    Получилось!) Сперва не совсем правильно понял, и удалил всю таблицу modx_transport_packages )) Сейчас залил БэкАп базы (уже со сломанного сайта, сделанной перед удалением таблицы. Там нашел его строку, удалил, заработало, как ты и сказал.

                                                                                    Спасибо!)

                                                                                    Сейчас попробую второй плагин)
                                                                                  2. Илья Уткин 13 июля 2018, 14:08 # 0
                                                                                    Вот, вроде, безглючный пакет: tvdisplayname-1.2-beta1.transport.zip — после того, как сайт починишь, сделай бэкап и попробуй его установить.
                                                                                    1. Amsterdam 13 июля 2018, 14:12 # 0
                                                                                      Просто удалить? Удалил, теперь в управлении пакетами «Нет данных для отображения». Ни один плагин не находится, однако фронт работает, все выводится корректно, значит плагины присутствуют и работают…
                                                                                      1. Amsterdam 13 июля 2018, 14:14 # 0
                                                                                        Или это потому, что остались в папках, не знаю. Что думаешь, поудалять их и все заново переустановить?
                                                                                        1. Amsterdam 14 июля 2018, 08:01 # 0
                                                                                          Илья, а откуда этот плагин? Не вижу его в репозиториях
                                                                                          1. Илья Уткин 16 июля 2018, 07:55 # 0
                                                                                            Это я просто Packman'ом запаковал сниппет getTvDisplayName. Потому что в коде сниппета ничего критичного нет, какая-то проблема именно с установщиком.
                                                                                            1. Amsterdam 16 июля 2018, 16:38 # 0
                                                                                              Спасибо тебе, теперь все работает! Может быть имеет смысл залить в репозиторий с адекватным установщиком и безошибочным кодом
                                                                                              1. Илья Уткин 16 июля 2018, 16:43 # 0
                                                                                                Присвоить авторство себе? =))

                                                                                                Лучше попробовать выйти на связь с автором и попросить его исправить ошибку. Или официально отдать другому разработчику.
                                                                                                1. Amsterdam 16 июля 2018, 17:26 # 0
                                                                                                  Ну ты же немного исправил код)) С комментарием «В основе лежит код плагина от такого то...» )

                                                                                                  Судя по дате обновления, которая, полагаю, и дата выхода плагина — 2011, и ошибках, которые в нем присутствуют, автор его давно забросил. А плагин то хороший, жаль что так пропадает.
                                                                                  3. Amsterdam 13 июля 2018, 13:28 # 0
                                                                                    (ERROR @ /core/model/modx/modprocessor.class.php: 256) Processor failed creating output array due to JSON error 5

                                                                        Авторизация

                                                                        через сервис Loginza:

                                                                        Подписка или RSS

                                                                        Буду присылать новые статьи — никакого спама



                                                                        Шаблоны MODX

                                                                        1 2 Дальше »

                                                                        Объектная
                                                                        модель
                                                                        MODX