Пишу топик с описанием новейших технологий, корни которых берут свое начало еще вот в этом топике, написанном более двух лет назад. Решил его перенести сюда. Почитайте пока, а я статью свою допишу. Она довольно интересная :)
Один из самых главных барьеров в переходе с MODX Evo на MODX Revo — это xPDO. Многим выносит мозг тот факт, что надо создавать физические файлы с какими-то классами, генерировать схему и много еще каких-то танцев с бубнами. «Невозможность» работать в полной мере с базой данных отпугивает очень многих, и многие продолжают разрабатывать на Эво, просто потому что там «проще», хотя и с соблазном смотрят на всякие плюшки Ревы, типа пакетов, источников файлов и т.п.
Но ответьте мне на такой вопрос: «Вы родились со знаниями того, как работать с mysql? Все сразу освоили mysql_connect(), mysql_select_db(), mysql_query() и т.д.и т.п.?» Согласитесь, что все это так же приходилось осваивать, и совсем не за один день.
Я сейчас приведу совсем небольшой, но очень и очень хитрый код (результат моих последних исследований xPDO и продолжение позавчерашней темы), а под катом вы узнаете очень много нового, и возможно кому-то работа с xPDO покажется еще проще, чем с mysql-функциями и библиотеками.
Что здесь происходит?
А происходит следующее: мы быстренько и на лету создали свой объект для взаимодействия с базой данных ( в частности с таблицей site_content (вместе с префиксом modx_site_content)), и извлекли из нее запись с id=1 (выполнили запрос).
Далее можно, к примеру, получить значения объекта. К примеру так:
Или так (сразу все данные):
Заметка: имя таблицы указано в описании объекта
Как это работает? (вкратце)
Мы создали классы page и page_mysql, расширяющие объект xPDOObject (так как именно объекты xPDOObject имеют необходимый функционал для взаимодействия с базой данных, типа load(),save(), remove() и т.п.)
Мы добавили описание своего объекта в окружение xPDO ($modx->map['page']) (не забывает, что MODx — это расширение класса xPDO).
Получили объект (выполнили запрос)$o=$modx->getObject('page', array( 'id' => 1 ));
Что это дает?
Такой подход позволяет выполнять взаимодействие с базой данных вообще из любого положения, будь то сниппет, плагин или даже во внешнем файле. Вот такой код работает:
Читай: самый быстрый коннектор с MODX-окружением и без лишних инициализаций классов-реквестеров-респонсеров, сессии и т.п. Можно еще облегчить, если напрямую подсосать конфиги и инициализировать не MODx класс, а xPDO.
Плюс к этому можно четко управлять какие колонки извлекать, а какие нет, и не бояться за повторные запросы (читаем про это здесь).
И вообще еще много чего дает, о чем я прям сейчас не буду писать, но в дальнейшем буду время от времени выкладывать примеры.
А почему не писать чистые SQL-запросы и не выполнять их через $modx->prepare($sql)?
Здесь несколько причин.
1. Префиксы таблиц в базе данных. По умолчанию они modx_, но не редкость, когда и отличаются. Даже если вы перед каждым запросом будет получать имя таблицы через API MODX-а, то это как минимум не удобно.
2. Написание чистых SQL-запросов так же требуют знаний, и не малых. В приведенном же случае достаточно просто знать структуру таблицы (какие есть колонки).
3. Просто выборка — это еще ладно, не сложно (select * from table;). А что вы будете делать, если вам надо обновить 28 колонок за раз? Могу сказать точно, что написание такого SQL-запроса так же займет не мало времени.
Есть еще причины, но этого, думаю, достаточно.
А почему сразу 2 класса надо, а не один?
Один класс — чисто базовый, содержащий дополнительный функционал. Второй класс — для своего типа базы данных, чтобы логику взаимодействия с БД можно было разделить. То есть если это mysql, то класс будет classname_mysql, если MSSQL SERVER, то classname_sqlsrv.
Более подробно с примерами.
Приведенный выше пример — это простейший вариант, позволяющий только делать выборки из БД. Для более полного взаимодействия с базой требуется описание колонок таблицы (я не нашел документации с описанием мета-данных xPDO-объектов, потому напишу при аказии мануал, а пока по примерам и копаем класс xPDOObject).
В итоге, если мы хотим, чтобы можно было сохранять данные объектов в БД, то нам понадобится описание колонок. К слову, при попытке сохранения объекта xPDO будет писать запись в таблицу, только она не будет содержать значений.
Итак, дополним наш класс
Здесь мы добавили массив-описание с мета-данными колонок. Зачем они нужны? Они позволяют определить xPDO-объекту какого типа данные хранятся на стороне базы данных, а какие типы на стороне php. Рассмотрим на примере описания колонки id:
Вот когда колонка четко описана, тогда xPDO знает как работать с данной таблицей, и можно сохранять данные в базу методом ->save();
Кстати, тут есть хитрость: дело в том, что ООП никто не отменял, и можно переопределять не только xPDOObject, но и его дочерние классы, к примеру xPDOSimpleObject. Что это нам дает? В xPDOSimpleObject уже описана колонка id (и ничего более кроме нее), и поэтому мы можем выкинуть ее из нашего описания. Вот описание объекта xPDOSimpleObject:
К слову, находится он в файле core/xpdo/om/mysql/xpdosimpleobject.map.inc.php, а не в model/modx/… Это вам гарантирует, что ваши объекты, расширяющие этот класс, будут работать даже за пределами MODX на чистом xPDO. Из MODX-объектов минимум 28 штук расширяют этот класс, дабы не плодить описание колонки id.
Обновим наш код, удалив описание колонки id.
Вот, уже компактней. Многие конечно могут посетовать на то, что описывая так объекты в своих сниппетах, они получатся громозскими. Но ведь и здесь можно играться. Создать несколько базовых элементарных классов, воткнуть их в один плагин, и расширять нужные. Ведь расширять и 10 классов можно. Кстати, несколько классов, воткнутых в плагин, будет быстрее работать, чем рассовывать их по файлам по классической модели, так как по классике получается на каждый объект по 3 файла чтение, а плагин — один. Читай: еще момент для оптимизации, хоть и не значительной.
В оставшихся колонках особо описание не отличается, только что типы данных другие, но на последок есть у меня еще фишка про запас:-)
Давайте посмотрим на код метода xPDOObject::save() (правда он не маленький)
То есть, если в описании колонки есть 'phptype'=>'password', то значение этой переменной автоматически будет закодировано методом $this->encode() Посмотрим на этот метод.
То есть если тип — password или md5, то значение будет закодировано в md5.
И не забывайте, что это ООП, то есть этот метод можно переопределить.
Или вот это:
То есть если тип — datetime или timestamp и указан атрибут 'ON UPDATE CURRENT_TIMESTAMP', то при сохранении объекта xPDO автоматически будет обновлять значение на текущее время.
К слову, это описание есть в класс modSystemSetting, и мы всегда видим время последнего обновления. При этом исключается человеческий фактор, что кто-то забудет обновить это поле. То есть даже если вы в своем сниппете сделаете так:
, время изменения записи зафиксируется.
В общем там еще многое всего очень и очень интересного, и я буду постепенно выкладывать новые материалы.
P.S. Если кто-то все еще считает, то xPDO сложно и вообще не заслуживает внимания, тот — не я.