Это решение проблемы для Symfony 1.1 и Propel 1.3, когда нужно из двух таблиц объединенных черех join получить все данные. Т.е. хотим выполнить такой запрос
SELECT news.NEWS_ID, news.CATEGORY_ID, news.TITLE, news.BODY, news.CREATED_AT, news_category.CATEGORY_ID, news_category.NAME FROM `news` LEFT JOIN news_category ON (news.CATEGORY_ID=news_category.CATEGORY_ID)
А вот с этим проблема, если таблицы не связаны через внешний ключ (foreign key), вывести данные из второй таблицы news_category проблематично, но возможно. В основе материал: Applying custom joins in doSelect* method. Но для новой версии материал не соответствует действительности. Чтобы все заработало, следует сделать следущее.
Рассмотрим ситуацию на примере. Есть 2-е таблицы, допустим, новости и категории новостей. Связь между таблицами через поле category_id.
Структура таблиц:
# Table "news" news_id category_id title body created_at # Table "news_category" category_id name
Мы создали модели, формы, БД:
$ symfony propel:build-model $ symfony propel:build-forms $ symfony propel:build-sql
Задача: сделать запрос и вывести все данные. Допустим сделать следующее
$c = new Criteria(); NewsPeer::addSelectColumns($c); NewsCategoryPeer::addSelectColumns($c); $c->addJoin(NewsPeer::CATEGORY_ID, NewsCategoryPeer::CATEGORY_ID, Criteria::INNER_JOIN); $news = CoreAclUserPeer::doSelect($c); # Выводим заголовок для первой записи, все отлично, а как же быть с <b>news_category.NAME</b>? echo $news[0]->getTitle();
РЕШЕНИЕ:
Нужно будет подправить несколько файлов, файлы в om и map не трогаем (напоминаю, что пример для версии симфони 1.1 и пропел 1.3):
• /lib/model/BaseNewsPeer.php
• /lib/model/BaseNews.php
BaseNewsPeer.php
# Добавляем атрибут и один метод protected $newsCategory = null; public static function doSelectJoinGroup(Criteria $c, $con = null, $join_behavior = Criteria::LEFT_JOIN) { $c = clone $c; // Set the correct dbName if it has not been overridden if ($c->getDbName() == Propel::getDefaultDB()) { $c->setDbName(self::DATABASE_NAME); } NewsPeer::addSelectColumns($c); $startcol = (NewsPeer::NUM_COLUMNS - NewsPeer::NUM_LAZY_LOAD_COLUMNS); NewsCategoryPeer::addSelectColumns($c); $c->addJoin(array(NewsPeer::CATEGORY_ID,), array(NewsCategoryPeer::CATEGORY_ID,), $join_behavior); $stmt = BasePeer::doSelect($c, $con); $results = array(); while ($row = $stmt->fetch(PDO::FETCH_NUM)) { // Hydrate the News object $omClass = NewsPeer::getOMClass(); $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1); $obj1 = new $cls(); $obj1->hydrate($row); // Hydrate the NewsCategory object $omClass = NewsCategoryPeer::getOMClass(); $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1); $obj2 = new $cls(); $obj2->hydrate($row, $startcol); // Add the $obj2 (NewsCategory) to $obj1 (News) $obj1->newsCategory = $obj2; $results[] = $obj1; } return $results; }
BaseNews.php
# добавляем метод public function getNewsCategory() { if(!$this->newsCategory) { $this->newsCategory = NewsCategoryPeer::retrieveByPk($this->getNewsId()); } return $this->newsCategory; }
После это переписываем наш запрос
$c = new Criteria(); $this->userList = NewsPeer::doSelectJoinGroup($c); # Выводим заголовок для первой записи echo $news[0]->getTitle(); # Выводим название категории echo $news[0]->getNewsCategory()->getName();
P.S.
Задача решена, к БД был выполнен только один запрос.
Протестировано на Symfony 1.1.2-DEV.
Август 15th, 2008 at 15:45
Одного не пойму. Зачем
$c = clone $c;
???
Август 15th, 2008 at 16:04
Мне тоже это не понятно немного. Вот как у них написано в комментах:
// we’re going to modify criteria, so copy it first
$criteria = clone $criteria;
Октябрь 4th, 2008 at 21:10
для того, чтобы вызов «doSelectJoinGroup» не менял передаваемую критерию (так как она может использоваться еще и для нахождения doCountXXX)
Октябрь 4th, 2008 at 21:18
очень понравился этот фрагмент кода:
$c->addJoin(array(NewsPeer::CATEGORY_ID,), array(NewsCategoryPeer::CATEGORY_ID,)
появилась возможность делать запросы для выборки языков, например:
$c->addJoin(array(LocalePeer::ID, LocaleI18nPeer::CULTURE), array(LocaleI18nPeer::LOCALE_ID, "'ru'"), Criteria::LEFT_JOIN);
SELECT .. FROM locale LEFT JOIN locale_i18n ON (locale.id = locale_i18n.locale_id AND locale_i18n.culture = 'ru')