"Классные" процессоры в MODX 2.2
Предлагаю вам свой очень вольный перевод записи из блога Mark Hamstra о новых процессорах, основанных на классах. Я буду называть их «классными» процессорами — так короче и точнее отражает их суть.
Одно из изменений в MODX 2.2 это новая, полностью переделанная система процессоров, основанных на классах («классные» процессоры), позволяющие вам cущественно упростить создание процессоров для компонентов. Как и любая обновка — эта позволяет вам использовать несколько новых фокусов.
Я использовал эти процессоры в своем новом проекте, и теперь хочу распространить эти наработки где только можно.
Что такое «классные» процессоры?
Если вы уже создавали компоненты для MODX, то вы знаете, что такое процессор — это php файл, который запускается AJAX коннектором (или modX::runProcessor), делает, все что от него требуется и возвращает ответ в виде JSON строки.
«Классные» процессоры упрощают эту работу. Например, вы можете использовать подобный код для обновления какого-то элемента:
Что делает modObjectUpdateProcessor?
Несмотря на минимум кода, который мы использовали, для его корректого выполнения происходит много всяких неочевидных процессов.
Разбираясь в классе modProcessor, я нашел 14 этапов для выполнения modObjectUpdateProcessor
Расширяем наш процессор для обработки чекбоксов
Когда вы сделали замечательное окно на modExt/ExtJS и прилепили туда активный чекбокс (xtype: 'checkbox') — оно может работать не так, как вы задумали. Возможно, ваша схема БД ожидает булево true или false (я знаю — у меня так!), которое будет сохранено в БД в поле с типом tinyint 1 или 0. А вот форма вам отправит поле чекбокса как «on», если он отмечен, или пустоту — если нет. Оба-на…
К счастью, у нас есть много функций, чтобы расширить процессор update и привести значение вредного чекбокса к нужному виду, для сохранения в объект через fromArray() как полагается.
Посмотрите снова на список из 14 шагов. Метод fromArray() запускается на 6м этапе, значит, нам надо поменять значение чекбокса до него. Как вы смотрите на «BeforeSet», в 5м этапе? По моему, должно сработать!
Если вы не особо знакомы с объектно-ориентированным программированием (ООП), то вся эта писанина может показаться вам непонятной фигней. На самом деле, мы просто переписываем родные методы стандартного процессора своими, чтобы изменить его как нам надо. В интернете навалом книжек по ООП в PHP, которые вы можете почитать, если интересно.
Итак, теперь мы меняем наш первый пример, чтобы изменить значение чекбокса при помощи «beforeSet»:
Мы можем сделать что-то подобное и с процессором remove, для предотвращения удаления элемента, если он все еще связан с другим элементом.
Например, следующий процессор не удалит объект «fdmCharacteristic», если тот еще связан с объектами «fdmRoomTypeCharacteristic».
Я использовал тип «checkbox», хотя в MODX с версии 2.1 есть тип «xcheckbox», который присылает значение (1 или 0, если мне не изменяет память). Не знаю, почему я не использовал его, но наверняка «классный» процессор будет работать и с ним. Просто не проверял, и я люблю все контролировать.
«Просто интересно… Вы работаете над компонентом по управлению недвижимостью?»
Как вы догадались?! :)
Что меня выдало: объект «fdmRoomType», «fdmRoomTypeCharacteristic» или «fdmProperty»? :)
Супер-пупер компонент, над которым я сейчас работаю называется FrontDeskMan и я надеюсь закончить его в течении нескольких недель. Он не будет доступен как Open Source, но наверняка мы будем продавать на енго лицензии и поддерживать его, как расширение для индустрии туризма и развлечений в моем совместном проекте с Jared Loman.
Вы уже можете готовить клиентов местных отелей к новому вебсайту с интегрированным управлением комнатами и бронированием, когда мы стартуем!
Одно из изменений в MODX 2.2 это новая, полностью переделанная система процессоров, основанных на классах («классные» процессоры), позволяющие вам cущественно упростить создание процессоров для компонентов. Как и любая обновка — эта позволяет вам использовать несколько новых фокусов.
Я использовал эти процессоры в своем новом проекте, и теперь хочу распространить эти наработки где только можно.
Что такое «классные» процессоры?
Если вы уже создавали компоненты для MODX, то вы знаете, что такое процессор — это php файл, который запускается AJAX коннектором (или modX::runProcessor), делает, все что от него требуется и возвращает ответ в виде JSON строки.
«Классные» процессоры упрощают эту работу. Например, вы можете использовать подобный код для обновления какого-то элемента:
<?php
class fdmPriceCodeUpdateProcessor extends modObjectUpdateProcessor {
public $classKey = 'fdmPriceCode';
public $languageTopics = array('frontdeskman:pricing');
public $objectType = 'fdmp';
}
return 'fdmPriceCodeUpdateProcessor';
… и MODX сделает всю работу за вас.Что делает modObjectUpdateProcessor?
Несмотря на минимум кода, который мы использовали, для его корректого выполнения происходит много всяких неочевидных процессов.
Разбираясь в классе modProcessor, я нашел 14 этапов для выполнения modObjectUpdateProcessor
- Стартует ваш процессор, устанавливаются его свойства.
- Запускается функция checkPermissions(), который вы можете использовать для проверки любых прав доступа.
- Запускается функция getLanguageTopics(), который возвращает массив языковых словарей, затем они загружаются.
- Стартует функция initialize(): она берет первичный ключ (из публичной переменной primaryKeyField, обычно это «id»), затем достает объект нужного класса по этому ключу, и, если это наследник modAccessibleObject, проверяет юзера на соответствие политике «save». Если initialize() не возвращает true — запрос отменяется, с ошибкой.
- Стартует beforeSet(): по умолчанию он просто проверяет, не установлено ли ошибок (например, так — $this->addFieldError('fieldname','error')). Если эта функция не возвращает true — запрос завершается с ошибкой.
- Вызывается fromArray() из объекта (он доступен через $this->object) для присвоения поученных при запуске свойств.
- Запускается beforeSave(). Как и beforeSet(), этот метод проверяет наличие ошибок. Если он не возвращает true — запрос возвращает сообщение об ошибке.
- Объект вызывает validate(). Если эта функция не возвращает положительное значение — выставляются ошибки в соответствие с проверенными значениями.
- Если вы выставили переменную beforeSaveEvent в своем классе, вызывается выставленное событие (invoke event) и если не возвращается true — сохранение не происходит. Учитывая, что fireBeforeSaveEvent() отмечена как публичная, обычно вам не нужно выставлять эту переменную.
- Вызывается saveObject(), которая просто делает save() объекта. В соответствии с комменариями в коде, эта функция может быть переназначена для каких-то особенных изменений объекта.
- Запускается fireAfterSaveEvent(), которая вызывает событие, указанное в переменной afterSaveEvent класса.
- Срабатывает logManagerAction(), для записи стандартного сообщения в Журнал системы MODX (вы его найдете в «Отчеты» → «Журнал системы управления»).
- Возвращается сообщение об успешном завершении процесса через функцию cleanup(). Она, на самом деле, просто оборачивает $modx->error->success/failure, который использовался в процессорах до версии 2.2.
- Ответ (успех или ошибка) парсится в объект modProcessorResponse и возвращается вам.
Расширяем наш процессор для обработки чекбоксов
Когда вы сделали замечательное окно на modExt/ExtJS и прилепили туда активный чекбокс (xtype: 'checkbox') — оно может работать не так, как вы задумали. Возможно, ваша схема БД ожидает булево true или false (я знаю — у меня так!), которое будет сохранено в БД в поле с типом tinyint 1 или 0. А вот форма вам отправит поле чекбокса как «on», если он отмечен, или пустоту — если нет. Оба-на…
К счастью, у нас есть много функций, чтобы расширить процессор update и привести значение вредного чекбокса к нужному виду, для сохранения в объект через fromArray() как полагается.
Посмотрите снова на список из 14 шагов. Метод fromArray() запускается на 6м этапе, значит, нам надо поменять значение чекбокса до него. Как вы смотрите на «BeforeSet», в 5м этапе? По моему, должно сработать!
Если вы не особо знакомы с объектно-ориентированным программированием (ООП), то вся эта писанина может показаться вам непонятной фигней. На самом деле, мы просто переписываем родные методы стандартного процессора своими, чтобы изменить его как нам надо. В интернете навалом книжек по ООП в PHP, которые вы можете почитать, если интересно.
Итак, теперь мы меняем наш первый пример, чтобы изменить значение чекбокса при помощи «beforeSet»:
class fdmPriceCodeUpdateProcessor extends modObjectUpdateProcessor {
public $classKey = 'fdmPriceCode';
public $languageTopics = array('frontdeskman:pricing');
public $objectType = 'fdmp';
public function beforeSet() {
$this->setProperty('active',($this->getProperty('active') == 'on'));
return parent::beforeSet();
}
}
return 'fdmPriceCodeUpdateProcessor';
Здесь мы использовали тернарный оператор вместе с функциями getProperty и setProperty от процессора. Но если вам нравится более подробный код — можно написать иначе, на результат это никак не влияет:/* ... */
public function beforeSet() {
$active = $this->getProperty('active');
if ($active == 'on') { $active = true; }
else { $active = false; }
$this->setProperty('active',$active);
return parent::beforeSet();
}
/* ... */
Мы можем сделать что-то подобное и с процессором remove, для предотвращения удаления элемента, если он все еще связан с другим элементом.
Например, следующий процессор не удалит объект «fdmCharacteristic», если тот еще связан с объектами «fdmRoomTypeCharacteristic».
<?php
class CharacteristicRemoveProcessor extends modObjectRemoveProcessor {
public $classKey = 'fdmCharacteristic';
public $languageTopics = array('frontdeskman:property');
public $objectType = 'rtc';
public function beforeRemove() {
$chars = $this->modx->getCount('fdmRoomTypeCharacteristic',array('characteristic' => $this->getProperty('id')));
if ($chars > 0) {
return $this->modx->lexicon('rtc.remove.roomtypesStillLinked');
}
return parent::beforeRemove();
}
}
return 'CharacteristicRemoveProcessor';
Последний пример на сегодня расширяет modObjectGetListProcessor для присоединения (join) таблицы и фильтрации по ее свойствам. Мы этого добиваемся изменением функции prepareQueryBeforeCount(). Так же, мы присваиваем еще немного переменных.<?php
class RoomTypeGetListProcessor extends modObjectGetListProcessor {
public $classKey = 'fdmRoomType';
public $languageTopics = array();
public $defaultSortField = 'name';
public $defaultSortDirection = 'ASC';
public $objectType = 'fdmRoomType';
/**
* @param xPDOQuery $c
* @return \xPDOQuery
*/
public function prepareQueryBeforeCount(xPDOQuery $c) {
$query = $this->getProperty('property');
if (!empty($query) && is_numeric($query)) {
$c->innerJoin('fdmRoomTypeProperty','RTP','fdmRoomType.id = roomtype');
$c->where(array(
'RTP.property' => (int)$query
));
}
return $c;
}
}
return 'RoomTypeGetListProcessor';
Замечание про чекбоксы в modExtЯ использовал тип «checkbox», хотя в MODX с версии 2.1 есть тип «xcheckbox», который присылает значение (1 или 0, если мне не изменяет память). Не знаю, почему я не использовал его, но наверняка «классный» процессор будет работать и с ним. Просто не проверял, и я люблю все контролировать.
«Просто интересно… Вы работаете над компонентом по управлению недвижимостью?»
Как вы догадались?! :)
Что меня выдало: объект «fdmRoomType», «fdmRoomTypeCharacteristic» или «fdmProperty»? :)
Супер-пупер компонент, над которым я сейчас работаю называется FrontDeskMan и я надеюсь закончить его в течении нескольких недель. Он не будет доступен как Open Source, но наверняка мы будем продавать на енго лицензии и поддерживать его, как расширение для индустрии туризма и развлечений в моем совместном проекте с Jared Loman.
Вы уже можете готовить клиентов местных отелей к новому вебсайту с интегрированным управлением комнатами и бронированием, когда мы стартуем!
Комментарии: 48
Перевел, как смог. Пишите ошибки в комментах — буду править.
во втором абзаце «чущественно»
fixed.
после 14 пункта в абзаце:
«каждый „классный“ процессор (create, update, getlist, remove и get) имеет собственные и методы» — думаю «и» не нужно >> «имеет свои собственные методы»
«каждый „классный“ процессор (create, update, getlist, remove и get) имеет собственные и методы» — думаю «и» не нужно >> «имеет свои собственные методы»
Спасибо!
Дочитайте до конца и давайте все правки сразу, списком =)
Дочитайте до конца и давайте все правки сразу, списком =)
ок! :)
все дочитал, перевод удачный, сравнивал некоторые отрывки с оригиналом, в общем понравилось, спасибо! :)
хотя, признаюсь, материал понятен пока на 70-80%, нужно будет еще почитать и вернуться, когда будет писать свои компоненты :)
пока увидел только еще одно исправление — «Когда вы сделали замечательно окно на modExt/ExtJS» — наверно, «замечаетльное»
все дочитал, перевод удачный, сравнивал некоторые отрывки с оригиналом, в общем понравилось, спасибо! :)
хотя, признаюсь, материал понятен пока на 70-80%, нужно будет еще почитать и вернуться, когда будет писать свои компоненты :)
пока увидел только еще одно исправление — «Когда вы сделали замечательно окно на modExt/ExtJS» — наверно, «замечаетльное»
Не пойму как вообще debug проводить. Вроде все делал по туториалу Doodles, только заменяя значения на свои таблицы и классы. В итоге табличка выводится, но пустая. Обращение к connector.php возвращает {«total»:«0»,«results»:[]}.
Плюс еще у меня $objectType никак не влияет на значения в лексиконе. Задаю к примеру $objectType = 'my_cmp.message' и $_lang['my_cmp.message_id'] = 'ID сообщения'. В табличке вывожу заголовок header: _('id') — получается пустой. А если выводить header: _('my_cmp.message_id') — он выводится.
Плюс еще у меня $objectType никак не влияет на значения в лексиконе. Задаю к примеру $objectType = 'my_cmp.message' и $_lang['my_cmp.message_id'] = 'ID сообщения'. В табличке вывожу заголовок header: _('id') — получается пустой. А если выводить header: _('my_cmp.message_id') — он выводится.
Я делаю просто — print_r($var); die;
И смотрю в консоли браузера что выводится коннектором.
Про лексикон ничего не могу сказать, тут нужно разбираться, а мне некогда.
И смотрю в консоли браузера что выводится коннектором.
Про лексикон ничего не могу сказать, тут нужно разбираться, а мне некогда.
А куда это вставить то можно, если идет class MessageGetListProcessor extends modObjectGetListProcessor {...} а потом сразу return MessageGetListProcessor;
Нашел в ошибках, что этот процессор запрашивает таблицу, начинающуюся на modx_ хотя у меня в схеме без него. В сниппетах все прекрасно работало. Подскажите, пожалуйста, где убирается этот префикс?
Нашел в ошибках, что этот процессор запрашивает таблицу, начинающуюся на modx_ хотя у меня в схеме без него. В сниппетах все прекрасно работало. Подскажите, пожалуйста, где убирается этот префикс?
Не подскажу.
Это перевод, а не лично мой опыт работы. Я пока только getlist пробовал погонять по исходникам самого MODX, проблем с префиксом не было, все нормально работало.
Могу посоветовать поглядеть продвинутые компоненты, которые используют эти «классные» процессоры.
Articles например — https://github.com/splittingred/Articles/tree/develop/core/components/articles/processors
Это перевод, а не лично мой опыт работы. Я пока только getlist пробовал погонять по исходникам самого MODX, проблем с префиксом не было, все нормально работало.
Могу посоветовать поглядеть продвинутые компоненты, которые используют эти «классные» процессоры.
Articles например — https://github.com/splittingred/Articles/tree/develop/core/components/articles/processors
Разобрался со всем, оказывается в сниппете addPackage был с пустым префиксом, а в процессорах не указан и выставляло по умолчанию modx_.
А $objectType только на ошибки в процессоре влияет.
А $objectType только на ошибки в процессоре влияет.
А как тогда сниппеты работали, с пустым префиксом?
Или в них модель отдельно подключали?
Или в них модель отдельно подключали?
Пустой имеется в виду подключал прямо в сниппете свою модель через addPackage(блаблабла, '');
2. Запускается функция checkPermissions(), который вы можете @@импользовать@@ для проверки любых прав доступа.
2. Запускается функция checkPermissions(), которую вы можете использовать для проверки любых прав доступа.
Почти освоил эту штуку. Только не получается сделать автоапдейт из таблицы (это как в системных настройках — меняешь и сразу запоминается). В методе modObjectUpdateProcessor::initialize() оказывается пустым $this->object и все тут…
В таких случаях я смотрю исходник и делаю как там.
Вот исходник автоапдейта системных параметров — https://github.com/modxcms/revolution/blob/develop/core/model/modx/processors/system/settings/updatefromgrid.class.php
Процессору шлется строка data, он его принимает, проверяет, преобразует из JSON в массив и выставляет переменные объекта.
Очень красиво.
Вот исходник автоапдейта системных параметров — https://github.com/modxcms/revolution/blob/develop/core/model/modx/processors/system/settings/updatefromgrid.class.php
Процессору шлется строка data, он его принимает, проверяет, преобразует из JSON в массив и выставляет переменные объекта.
Очень красиво.
Меня как раз это осенило и хотел сообщить об этом)) даже файл тот же смотрел :)) Странно, что они не вынесли UpdateFromGridProcessor отдельно.
Вопрос по «классным» процессорам.
Пытаюсь получить только данные из двух столбцов таблицы, пишу вот в этот метод след. код
public function prepareQueryBeforeCount(xPDOQuery $c)
$c->select(array('id','pagetitle'));
Все равно выбираются все столбцы из таблицы. Я уже и называние класса добавлял перед названием столбца, тот же результат. Может быть у вас есть решение?
Т.к. только из-за этого пока не могу полностью перейти на классные процессоры
Пытаюсь получить только данные из двух столбцов таблицы, пишу вот в этот метод след. код
public function prepareQueryBeforeCount(xPDOQuery $c)
$c->select(array('id','pagetitle'));
Все равно выбираются все столбцы из таблицы. Я уже и называние класса добавлял перед названием столбца, тот же результат. Может быть у вас есть решение?
Т.к. только из-за этого пока не могу полностью перейти на классные процессоры
Получилось, нужно было переопределить в расширении класса две функции getData() и prepareRow(), т.к. первая функция возвращает объект, а вторая его ожидает. Изменил так чтобы getData() возвращала массив.
Но по кол-ву строк получилось так же как и в процедурном стиле.
А время отклика через «классный» процессов выше (т.к. там куча проверок и событий) чем через процедурный.
А время отклика через «классный» процессов выше (т.к. там куча проверок и событий) чем через процедурный.
$c->select('id,pagetitle') — одна строка, через запятую.
Покажутся все столбцы, так как это записано в модели, но реально выберутся данные только из указанных — остальные должны быть пусты.
Дальше да, prepareRow() и всех делов.
Покажутся все столбцы, так как это записано в модели, но реально выберутся данные только из указанных — остальные должны быть пусты.
Дальше да, prepareRow() и всех делов.
В том то и дело что выбираются данные из всех столбцов. Я бы не постил, если бы не попробовал сам различные варианты.
Ниже иллюстрации.
i26.fastpic.ru/big/2012/1011/f5/4421f51f21031820061407016b0e33f5.png
i26.fastpic.ru/big/2012/1011/5b/f592963e8b248c963eb4d64c97861f5b.png
Ниже иллюстрации.
i26.fastpic.ru/big/2012/1011/f5/4421f51f21031820061407016b0e33f5.png
i26.fastpic.ru/big/2012/1011/5b/f592963e8b248c963eb4d64c97861f5b.png
Сделал в getData $c->prepare(); echo $c->toSql();die;
SELECT `id`,`pagetitle`,`parent` FROM `modx_site_content` AS `modResource` WHERE ((`modResource`.`published` = 1 AND `modResource`.`deleted` = 0) AND `modResource`.`id` IN (13,14,52)) ORDER BY `modResource`.`menuindex` ASC LIMIT 20
То есть, SQL запрос в getCollection попадает верный, а вот почему оно выбирает все столбцы, вместо указанных — мне не ведомо.
Проверил — и в сниппете так же, и в методе getIterator(). Похоже, с каких-то пор они просто игнорируют SELECT.
То ли баг, то ли так и надо.
SELECT `id`,`pagetitle`,`parent` FROM `modx_site_content` AS `modResource` WHERE ((`modResource`.`published` = 1 AND `modResource`.`deleted` = 0) AND `modResource`.`id` IN (13,14,52)) ORDER BY `modResource`.`menuindex` ASC LIMIT 20
То есть, SQL запрос в getCollection попадает верный, а вот почему оно выбирает все столбцы, вместо указанных — мне не ведомо.
Проверил — и в сниппете так же, и в методе getIterator(). Похоже, с каких-то пор они просто игнорируют SELECT.
То ли баг, то ли так и надо.
Интересно)
Может быть xPDO сначала кэширует все данные, а потом уже из кэша их достает. Но все равно не понятно почему он игнорирует перечисленные столбцы в SELECT
Мое мнение — глюк.
Раньше точно работало — проверял. Сейчас нет времени разбираться.
Раньше точно работало — проверял. Сейчас нет времени разбираться.
Возможно и глюк.
версия MODX 2.2.5
версия MODX 2.2.5
Проверял только что свою догадку, думал данные извлекаются из кэша, оказалось что нет. В кэше появляется только тот документ на котором я вызываю сниппет pastebin.com/mcR3Nr7H и почему-то всегда ресурс с id=1 кэшируется, остальных ресурсов в кэше нет.
Подозреваю, потому, что id=1 — это site_start.
Проблема явно не в кэш, там надо исходники копать. Правда, не ясно зачем, если вопрос уже решен?
Проблема явно не в кэш, там надо исходники копать. Правда, не ясно зачем, если вопрос уже решен?
Я как раз изучаю исходники, просто интереса ради.
Да id=1 это site_start, дефолтная установка 2.2.5
Да id=1 это site_start, дефолтная установка 2.2.5
Кстати есть такой метод чтобы конвертировать SQL запрос в объект xPDO
$modx->getCriteria($className, $criteria, $cacheFlag);
Т.е. обратный процесс методу toSQL()
$modx->getCriteria($className, $criteria, $cacheFlag);
Т.е. обратный процесс методу toSQL()
А это процессор который получился
pastebin.com/j7JBtHLr
pastebin.com/j7JBtHLr
Вот то что возвращает получившийся процессор, то что нужно
i25.fastpic.ru/big/2012/1011/54/b8fc5426ff408e5e197906a73643c954.png
i25.fastpic.ru/big/2012/1011/54/b8fc5426ff408e5e197906a73643c954.png
Немного неясно как делать соединения.
Сделал innerJoin как в последнем примере. Смотрю в консоли — поля связанной таблицы по-прежнему не возвращает. Может быть нужно в $c->select прописывать в классе getlist?
Сделал innerJoin как в последнем примере. Смотрю в консоли — поля связанной таблицы по-прежнему не возвращает. Может быть нужно в $c->select прописывать в классе getlist?
Да, конечно нужно не только объединить таблицы, но и выбрать из них нужные данные.
Вот такой код не срабатывает — results 0.
public function prepareQueryBeforeCount(xPDOQuery $c) {
$c->select(array('id','name','TdTypes','updated','enabled'));
$c->innerJoin('TdTypes','TdTypes','TdItems.tid=TdTypes.id');
return $c;
}
Пробовал и такой вариант:$c->select(array('columns'=>array('id','name','TdTypes','updated','enabled')));
и такой$c->select('id','name','TdTypes','updated','enabled');
Может быть определяю неверно, может не в той функции определяю. Подскажи, пожалуйста.
xPDOQuery::select() принимает только строку. В документации была опечатка, уже поправил.
То есть:
То есть:
$c->select('id,name,TdTypes,updated,enabled');
Попробовал строкой. В консоль попало только это
{"total":"1","results":[]}
Если закомментировать строку с select, то выводится это{"total":"1","results":[{"id":14,"enabled":1,"created":"-1-11-30 00:00:00","updated":"-1-11-30 00:00:00","ended":"-1-11-30 00:00:00","cid":3,"tid":3,"fid":0,"name":"\u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439","description":""]}}
Функция целиком выше.
Смотри лог, по любому выбираешь несуществующий столбец.
xPDO такие ошибки пишет в системный журнал.
xPDO такие ошибки пишет в системный журнал.
Попробуй еще
$c->select($this->modx->getSelectColumns('TdTypes','TdTypes'));
Вот так
Только поле name таблицы TdTypes затерло поле name таблицы TdItems при выводе. Видимо в алиасах дело. Поразбираюсь, спасибо за помощь.
$c->select($this->modx->getSelectColumns('TdTypes','TdTypes'))
как раз получилось. Только поле name таблицы TdTypes затерло поле name таблицы TdItems при выводе. Видимо в алиасах дело. Поразбираюсь, спасибо за помощь.
Крайне полезно делать
$c->prepare();
echo $c->toSql();
die;
Будет дамп SQL запроса, который можно загонять для проверки в phpmyadmin.
Не знаю пригодится ли кому-нибудь эта конструкция, но вот так я связал три таблицы. Полагаю есть более элегантные решения, но я пока до них не дошел.
public function prepareQueryBeforeCount(xPDOQuery $c) {
$c->select($this->modx->getSelectColumns('TdTypes','TdTypes','TdTypes_',array('name')));
$c->select($this->modx->getSelectColumns('TdCompanies','TdCompanies','TdCompanies_',array('name')));
$c->select($this->modx->getSelectColumns('TdItems','TdItems'));
$c->innerJoin('TdTypes','TdTypes','TdItems.tid=TdTypes.id');
$c->innerJoin('TdCompanies','TdCompanies','TdItems.cid=TdCompanies.id');
//$c->prepare();
//echo $c->toSql();
//die;
return $c;
}
Эта процедура дает такой запросSELECT `TdTypes`.`name` AS `TdTypes_name`, `TdCompanies`.`name` AS `TdCompanies_name`, `TdItems`.`id`, `TdItems`.`enabled`, `TdItems`.`created`, `TdItems`.`updated`, `TdItems`.`ended`, `TdItems`.`cid`, `TdItems`.`tid`, `TdItems`.`fid`, `TdItems`.`name`, `TdItems`.`description` FROM `modx_tenders_tdItems` AS `TdItems` JOIN `modx_tenders_tdTypes` `TdTypes` ON TdItems.tid=TdTypes.id JOIN `modx_tenders_tdCompanies` `TdCompanies` ON TdItems.cid=TdCompanies.id
Поздравляю!
Лично я не знаю более элегантного решения объединить 3 таблицы в одной выборке, чем join.
Кстати, можно немного упростить, выбирая поля псевдонимами:
Лично я не знаю более элегантного решения объединить 3 таблицы в одной выборке, чем join.
Кстати, можно немного упростить, выбирая поля псевдонимами:
$c->select('TdTypes.name as td_name');
$c->select('TdCompanies.name as comp_name');
Да, такая конструкция гораздо лучше — вчера пробовал, почему то строку не принимало как надо. Сейчас все ок.
Журнал пустой. Ошибок нет.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.