Разделы

Знакомство с xPDO

Давненько уже «не брал в руки перо», но очень хочется систематизировать свои знания и написать, наверно, один из первых пошаговых руководств по работе с ORB (не знаю как точно, но я называю такие вещи — ActiveRecord) фрэймворком xPDO (на котором построена MODx Revolution). Может быть это и не фрэймворком во всей мощи этого понимания, но по моему мнению с возложенными на себя задачами он справляется великолепно.
Что такое AR/ORM ?
Для тех кто в танке — ru.wikipedia.org/wiki/ActiveRecord (http://ru.wikipedia.org/wiki/ORM)
А если простыми словами — ActiveRecord освобождает вам от необходимости писать SQL запросы вручную, и делать выборки, просто работайте с переменными-объектами, свойтсва объектов представляют «столбцы таблицы», экземпляры объектов — строки. (Вобще звучит мутно и непонятно, лучше один раз увидеть и полюбить навсегда :) )

Несколько месяцев назад я остро ощутил потребность в AR фрэймворке для уменьшения рутинной работы с базой данных в проектах не связанных с МОДх. Опенсорс вариантов существует, конечно же масса. Но полновесный фрэймворк (типа Zend Framework или Yii) мне тянуть не хотелось (для поставленных задач). Соответсвенно вспомнил про творение Джейсона Коварда — xPDO.
Долго я оттягивал момент разирательства и когда всё же решил — упёрся в самое больное место господ Коварда и МакКормика — осутствие внятных рабочих примеров в документации к их замечательным скриптам. (Наличие доволно объемной официальной документации svn.modxcms.com/docs/display/xPDO20/Home мало проясняло ситуацию :) особенно в той части как работать с xPDO проекте вне MODx). Лично мне пришлось два раза перечитать почти все темы на форуме в разделе xPDO, чтобы «завести эту берданку» (честно был момент когда я хотел бросить эту гиблую затею :) ). Этим опытом и поделюсь ниже. (При этом настоятельно советую просмотреть оф. документацию т.к. я сейчас не буду вдаваться в подробности и тонкости, главное дать рабочий базовый пример)

Подготовительный этап
Для начала возьмём базу данных для экспериментов (можете взять любую вашу), я же экспериментирую на Sakila sample database.
Как её скачать и установить расписано по ссылке (немножко через задницу всё делается, но зато в вашем распоряжении готовая громадная БД с множеством связей, можно практиковать очень изощренный запросы :) ).
Далее качаем сам скрипт xPDOwww.xpdo.org/downloads.html (на данный момент я взял xPDO-2.0.0-rc1).

Приступаем
Считаем, что БД вы установили правильно и через phpMyAdmin она нормально доступна. Считаем, что БД имеет имя "sakila", имя пользователя БД "root" и пароль тоже "root".
Разархивируем xPDO наш тестовый сервер в папку "xpdo", скажем.
Также создадим заранее папки «models» и «cache» рядом с «xpdo».

→ Создаем в корне рядом с папкой «xpdo» файл «dbConnection.php» с содержимым
<?php 
include("xpdo/xpdo.class.php");

$xpdo= new xPDO('mysql:host=localhost;dbname=sakila',"root","root",
					array (
						XPDO::OPT_CACHE_PATH => 'cache/',
						XPDO::OPT_TABLE_PREFIX => '',
						XPDO::OPT_HYDRATE_FIELDS => true,
						XPDO::OPT_HYDRATE_RELATED_OBJECTS => true,
						XPDO::OPT_HYDRATE_ADHOC_FIELDS => true,
						XPDO::OPT_VALIDATE_ON_SAVE => true,
					),
					array (
						PDO::ATTR_ERRMODE => PDO_ERRMODE_SILENT,
						PDO::ATTR_PERSISTENT => false,
						PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
					)
				); 
?>

Здесь происходит создание объекта класса xPDO (с которым потом всё время и работать) в строке mysql:host=db_host;dbname=db_name',«db_login»,«db_pass» замените если требуется на ваши данные.
О предназначении остальных массивов можно почитать в официальной документации — сейчас важен только параметр XPDO::OPT_CACHE_PATH => 'cache/' он указывает на папку для кэша, которую мы создали выше (можете изменить если надо).

→ создаём файл «dbGenerator.php» с содержимым
<?php
include("dbConnection.php");
$manager= $xpdo->getManager();
$generator= $manager->getGenerator();
$xml= $generator->writeSchema('models/MyDBModel.mysql.schema.xml', 'MyDBModel', 'xPDOObject', '');
?>

а теперь запустите скрипт dbGenerator.php (он произведет так называемый reverse-engineer схемы по уже готовой БД)
да вы видите белый экран броузера (если не произошло ошибки :)), но так и должно быть.
Откройте папку «models» и о чудо! там появился файл MyDBModel.mysql.schema.xml это файл схемы БД, его можно менять при необходимости о том, что и как можно менять тоже можно найти в документации (я честно говоря пока сам не полностью разобрался, поэтому не берусь учить, сейчас мы обойдёмся без изменений).

→ далее создаем файл «dbParser.php» с содержимым
<?php
include("dbConnection.php");
$manager= $xpdo->getManager();
$generator= $manager->getGenerator();
$generator->parseSchema('models/MyDBModel.mysql.schema.xml','models/');
?>

запустив теперь этот файл вы проведёте так называемый forward-engineer ранее сгенерированной схемы в готовую к употреблению модель
загляните в папку «models» заметьте появившуюся папку «MyDBModel» — там и лежит модель нашей БД отформатированная специальным образом для быстрого и эффективного использования.

Этап подготовки закончен. Теперь радуемся чудесам фрэймворка.

Например
создаем файл «test.php» с содержимым
<?php
include("dbConnection.php");
$xpdo->addPackage('MyDBModel','models/','');

$name = $xpdo->getObject('Actor', 1);
if($name)
{
	echo "Имя актёра - ".$name->first_name." ".$name->last_name." / ".$name->last_update;
}
else
{
	echo "Ничего не найдено :(";
}
?>

Вот и произошла уличная магия — на экране вы увидели «Имя актёра — PENELOPE GUINESS / 2006-02-15 04:34:33».
А теперь по порядку — сначала строка $xpdo->addPackage('MyDBModel','models/',''); говорит скрипту, что сейчас мы хотим работать с моделью МyDBModel из папки models/ (мы её предварительно сгенерировали).
$name = $xpdo->getObject('Actor', 1);
производит выборку строки из таблицы Actor в модели МyDBModel с primary key (обычно это поле id) = 1 (поставьте другое число и увидите, что результат будет другой).
Далее проверка ну пустой результат и вывод данных first_name, last_name и last_update. Внимательный читатель конечно же понял, что эти свойства объекта $name есть имена столбцов в нашей таблице Actor. И так можно доставать любые столбцы в любых таблицах (в рамках нашей модели, конечно же).

Идём дальше — Как выбрать сразу многострок с опр. значением любого столбца, не только ключа ?
$collect = $xpdo->getCollection('Actor',array('first_name'=>'PENELOPE') );
foreach($collect as $actor)
{
	echo $actor->first_name." ".$actor->last_name."
";
}

выбираем всех актеров с именем PENELOPE.
Задавать параметры для запроса в виде массива (здесь array('first_name'=>'PENELOPE') ) можно и в функции getObject. Вобще функции довольно гибкие и почти все поддерживают разные варианты обращения. Самый верный помощник в этом деле — какой нибудь PHP IDE с автокомплитом кода (NetBeans, например, сам выводит возможные варианты и параметры функций).

а теперь создадим новую запись об актере
$newActor = $xpdo->newObject('Actor',array(  
    'first_name' => 'Star',  
    'last_name' => 'SuperStar',   
));  
if($newActor->save())
{
	echo "Запись успешно добавлена !";
}

Запись в БД происходит при вызове $newActor->save(), там же и производим проверку на успех.

И конечно же удалим наделанное :)
$actor = $xpdo->getObject('Actor',array('first_name'=>'Star'));  

if ($actor->remove()) {  
    echo 'Удаление прошло успешно !';  
}

Философия проста — чтобы чтото удалить, нужно это найти и получить, а потом уж «резать». А также угадайте с трёх раз зачем нужен метод removeCollection.

для изменения строк есть метод set
$actor = $xpdo->getObject('Actor', 1);
echo "До - ".$actor->first_name;  
$actor->set('first_name','IVAN');
$actor->save();
$actor = $xpdo->getObject('Actor', 1);
echo "
 После - ".$actor->first_name; 


Думаю для начала хватит.
Там осталось ещё много интересностей типа расширенного запроса «newQuery» и методов быстрого «доставания» связанных данных из разных таблиц (getObjectGraph, getCollectionGraph и другие). Но с этим я разберусь и изложу чуть позже.

И на закуску кэширование произвольной инфы
$xpdo->getCacheManager();
$str = 'My cached data text.';  
$xpdo->cacheManager->set('myVar',$str,3600);
echo $xpdo->cacheManager->get('myVar');  
//$xpdo->cacheManager->delete('myVar'); 

Хранится всё это в папке «cache», которую мы задали при инциализации соединения (посмотрите напоявившийся там файл).
Думаю в этом коде всё пердельно ясно (3600 — это кол-во секунд на которые кэш будет действовать, после этого он очищется, так что в иделае нужно сделать проверку на пустоту). Тонкость в том, что вызов $xpdo->getCacheManager(); в документации отсутсвует, но без этой строки у меня кэш упорно отказывался работать… может быть я что-то не так делаю? :)

Ссылки:
Знакомство с xPDO ч.2 или xPDO для «гиков»
  • +7
  • 22 января 2010, 02:05
  • iJack

Комментарии (7)

RSS свернуть / развернуть
0
просто работайте с переменными-объектами, свойтсва объектов представляют «столбцы таблицы», экземпляры объектов — строки.

по-моему, подобная штука реализована в SilverStripe
avatar

Solo

  • 22 января 2010, 06:43
0
Вполне может быть, реализация модели Active Record сейчас встречается почти везде. Ибо это удобно, хотя и требует больших ресурсов — но для не очень больших нагрузок это не критично как я понимаю.
avatar

iJack

  • 22 января 2010, 12:15
0
но для не очень больших нагрузок это не критично как я понимаю.


эм, а как же Revo, под который, если я правильно понимаю, xPDO и делали?

Или кеширование всех спасет?
avatar

pitbull

  • 22 января 2010, 21:30
0
Думаю, что «не очень больших нагрузок» — это вплоть до тех масштабов проекта, когда использование готовой ЦМС вобще становистя нецелесообразным. И то… даже в таких проектах используется та или иная реализация Active Record.
Просто любая надстройка наш штатными возможностями PHP — это по определению расход доп. памяти и процессорного времени.
А как известно Рэво имеет официальные минимальные системные требования куда большие чем для Эво.
avatar

iJack

  • 24 января 2010, 15:55
0
Браво, iJack!
avatar

pitbull

  • 22 января 2010, 08:50
0
а теперь создадим новую запись об актере


Кстати, а ID созданной записи как получить можно. Он возвращает его как результат $newActor->save() или через $newActor->id?
avatar

Carw

  • 17 февраля 2010, 12:54
+1
Сначала делаем $newActor->save();, а потом уже можно получить через $newActor->actor_id;

$newActor->save() — как результат выдает 1 в случае удачной операции.
avatar

iJack

  • 17 февраля 2010, 13:28

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.