Не так давно появился вопрос: а могут ли orm нормально работать с cross-db запросами?.. И вот пришлось дописывать проект на ZF, в котором надо было это использовать. Недолго думая, взял Propel (на тот момент 1.6.0.beta2) и попробовал реализовать нормальную его работу в связке с ZF.
Под катом — пример интеграции propel1.6 в zf1.11.х (актуально так же для нескольких предыдущих версий), а так же особенности Propel1.6 при обработке cross-db запросов.
(я делал все под Денвером, потому описано для него, разница минимальная)
распаковываем (например, в z:\lib\zf) создаем проект:
В дальнейшем упоминание test в пути будет аналогом z:\home\test
%zf%/library/Zend в test/library
test/public в test/www, рестартим Денвер, проверяем test (должна отобразиться стартовая ZF).
(я рекомендую через PEAR, для этого убеждаемся, что у нас установлен PEAR и прописан путь к нему в %path%, если нет — делаем и ребутимся):
(на момент написания — 1.6.0 stable), распаковываем содержимое папки runtime/lib из архива в test/library/Propel
— test/build.properties
— test/runtime-conf.xml
В test/www/index.php, после
дописываем
В принципе это все, можем переходить к примерам использования (в частности к работе с cross-db запросами).
Пускай у нас есть 2 блога. База юзеров для них — общая, контент — разный (то есть используем три базы, одну для юзеров и две собственно под контент сайтов, названия баз пусть соответственно будут test_users, test_blog1, test_blog2).
(пример глупый и не практичный, но это только пример)
— test/users.schema.xml
— test/blog1.schema.xml
— test/blog2.schema.xml
и получаем… ошибку.
Ну что же, раз уж нам хочется, чтоб оно работало, придется чуток подправить Column.php (ничего страшного не случиться пока вам не взбредет в голову два идентичных ключа сделать, честно):
Да, кстати, не забываем создать базы:
а теперь снова
Генерим sql:
В конец файла test/propel-data/sql/sqldb.map добавляем:
Создаем файл test/propel-data/sql/users.data.sql:
И вставляем структуру+данные:
контроллер:
вид:
Больше примеров использования related queries на propelorm.org.
Исходники можно посмотреть здесь.
Под катом — пример интеграции propel1.6 в zf1.11.х (актуально так же для нескольких предыдущих версий), а так же особенности Propel1.6 при обработке cross-db запросов.
Ставим Zend:
(я делал все под Денвером, потому описано для него, разница минимальная)
1. Качаем ZF,
распаковываем (например, в z:\lib\zf) создаем проект:
z:\home>z:\lib\zf\bin\zf create project test
В дальнейшем упоминание test в пути будет аналогом z:\home\test
2. Копируем
%zf%/library/Zend в test/library
3. переименовываем
test/public в test/www, рестартим Денвер, проверяем test (должна отобразиться стартовая ZF).
Интеграция Propel:
1. Устанавливаем propel-generator
(я рекомендую через PEAR, для этого убеждаемся, что у нас установлен PEAR и прописан путь к нему в %path%, если нет — делаем и ребутимся):
> pear channel-discover pear.phing.info
> pear install phing/phing-2.3.3
> pear channel-discover pear.propelorm.org
> pear install propel/propel_generator
2. Качаем последнюю версию Propel
(на момент написания — 1.6.0 stable), распаковываем содержимое папки runtime/lib из архива в test/library/Propel
3. Создаем файлы:
— test/build.properties
propel.project = propel
propel.packageObjectModel = true
propel.output.dir = ${propel.project.dir}/propel-data
propel.php.dir = ${propel.project.dir}/application/models
propel.phpconf.dir = ${propel.project.dir}/application/configs
propel.database = mysql
propel.database.url = mysql:host=localhost;dbname=test_users
propel.database.user = root
propel.database.encoding = utf8
— test/runtime-conf.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<propel>
<datasources default="test">
<datasource id="test">
<adapter>mysql</adapter>
<connection>
<dsn>mysql:host=localhost;dbname=test_users</dsn>
<user>root</user>
<password></password>
<settings>
<setting id="charset">utf8</setting>
</settings>
</connection>
</datasource>
</datasources>
</propel>
</config>
4. Собственно интеграция
В test/www/index.php, после
require_once 'Zend/Application.php';
дописываем
require_once 'Propel/Propel.php';
Propel::init(APPLICATION_PATH.'/configs/propel-conf.php');
set_include_path(APPLICATION_PATH.'/models' . PATH_SEPARATOR . get_include_path());
В принципе это все, можем переходить к примерам использования (в частности к работе с cross-db запросами).
Примеры:
Пускай у нас есть 2 блога. База юзеров для них — общая, контент — разный (то есть используем три базы, одну для юзеров и две собственно под контент сайтов, названия баз пусть соответственно будут test_users, test_blog1, test_blog2).
(пример глупый и не практичный, но это только пример)
1. Схемы:
— test/users.schema.xml
<database name="test" defaultIdMethod="native" schema="test_users" package="users">
<table name="user">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="username" type="varchar" size="255" required="true" />
<column name="email" type="varchar" size="255" required="true" />
</table>
</database>
— test/blog1.schema.xml
<database name="test" defaultIdMethod="native" schema="test_blog1" package="blog1">
<table name="blogpost" phpName="Blog1Blogpost">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="user_id" type="integer" required="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="text" type="CLOB" required="true" />
<foreign-key foreignTable="user" foreignSchema="test_users">
<reference local="user_id" foreign="id" />
</foreign-key>
</table>
</database>
— test/blog2.schema.xml
<database name="test" defaultIdMethod="native" schema="test_blog2" package="blog2">
<table name="blogpost" phpName="Blog2Blogpost">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="user_id" type="integer" required="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="text" type="CLOB" required="true" />
<foreign-key foreignTable="user" foreignSchema="test_users">
<reference local="user_id" foreign="id" />
</foreign-key>
</table>
</database>
2. Пробуем:
> propel-gen
и получаем… ошибку.
Nesting level too deep - recursive dependency?
Ну что же, раз уж нам хочется, чтоб оно работало, придется чуток подправить Column.php (ничего страшного не случиться пока вам не взбредет в голову два идентичных ключа сделать, честно):
public function hasReferrer(ForeignKey $fk)
{
return false;
}
Да, кстати, не забываем создать базы:
CREATE DATABASE `test_users` ;
CREATE DATABASE `test_blog1` ;
CREATE DATABASE `test_blog2` ;
а теперь снова
> propel-gen
3. Тестовые данные:
Генерим sql:
> propel-gen sql
В конец файла test/propel-data/sql/sqldb.map добавляем:
users.data.sql=test
Создаем файл test/propel-data/sql/users.data.sql:
INSERT INTO `test_users`.`user` (`id`,`username`,`email`) VALUES (1,'test','test@test.ru');
INSERT INTO `test_users`.`user` (`id`,`username`,`email`) VALUES (2,'user','user@test.ru');
INSERT INTO `test_blog1`.`blogpost` (`id` ,`user_id` ,`title` ,`text` )
VALUES
(NULL , '1', 'test blog post', 'just a test'),
(NULL , '1', 'one more test blog post', 'just a test'),
(NULL , '1', 'and one more test blog post', 'just a test');
И вставляем структуру+данные:
> propel-gen insert-sql
4. Наслаждаемся:
контроллер:
$user = new User();
$user->setUsername("pupkin");
$user->setEmail("pupkin@pupkin.ru");
$blogpost1 = new Blog1Blogpost();
$blogpost1->setTitle("Pupkin's post");
$blogpost1->setText("just for fun");
$blogpost2 = new Blog2Blogpost();
$blogpost2->setTitle("Pupkin's post in other blog");
$blogpost2->setText("it's quite easy, yeah");
$user->addBlog1Blogpost($blogpost1);
$user->addBlog2Blogpost($blogpost2);
$user->save();
$this->view->blog1 = Blog1BlogpostQuery::create()->leftJoinUser()->find();
$this->view->blog2 = Blog2BlogpostPeer::doSelectJoinUser(new Criteria);
вид:
<h1>First blog:</h1>
<?php foreach($this->blog1 as $blogpost): ?>
<div>
<h4><?php echo $blogpost->getTitle(); ?></h4>
<p><?php echo $blogpost->getText(); ?></p>
<div>added by <?php echo $blogpost->getUser()->getUsername() ?>, <?php echo $blogpost->getUser()->getEmail() ?></div>
</div>
<?php endforeach; ?>
<h1>Second blog:</h1>
<?php foreach($this->blog2 as $blogpost): ?>
<div>
<h4><?php echo $blogpost->getTitle(); ?></h4>
<p><?php echo $blogpost->getText(); ?></p>
<div>added by <?php echo $blogpost->getUser()->getUsername() ?>, <?php echo $blogpost->getUser()->getEmail() ?></div>
</div>
<?php endforeach; ?>
Больше примеров использования related queries на propelorm.org.
Исходники можно посмотреть здесь.