Как стать автором
Обновить

Propel: интеграция в ZF и работа с cross-db запросами

Не так давно появился вопрос: а могут ли orm нормально работать с cross-db запросами?.. И вот пришлось дописывать проект на ZF, в котором надо было это использовать. Недолго думая, взял Propel (на тот момент 1.6.0.beta2) и попробовал реализовать нормальную его работу в связке с ZF.

Под катом — пример интеграции 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.
Исходники можно посмотреть здесь.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.