Symfony: Propel предзагрузка объектов (preload objects)
Цель: уменьшить количество запросов для таблиц использующих много внешних ключей.
Описание проблемы: имеем 4 таблицы: news, news_i18n, news_type и news_type_i18n. На странице нужно отобразить список из 20 новостей, используя все перечисленные таблицы. А это 21 запрос. Методы doSelectWithI18n и doSelectJoinAll не позволяют получить значения i18n NewsTypePeer для NewsPeer с минимальным количеством запросов к БД.
Решение проблемы: вместо 21 запроса к БД выполнить 2. Механизм — использовать предзагрузку объектов NewsTypePeer со значениями i18n.
Листинг 1: schema.yml
propel: _attributes: { package: lib.model.news } news_type: _attributes: { isI18N: true, i18nTable: news_type_i18n } id: ~ url_segment: { type: varchar, size: 255, required: true } news_type_i18n: title: { type: varchar, size: 255, required: true } news: _attributes: { isI18N: true, i18nTable: news_i18n } id: ~ news_type_id: { type: integer, default: 0, foreignTable: news_type, foreignReference: id } url_segment: { type: varchar, size: 255, required: true } is_published: { type: boolean, required: true, default: 0 } published_at: { type: timestamp, index: true } image: { type: varchar, size: 255, required: true } video: { type: varchar, size: 255, required: true } created_at: ~ news_i18n: title: { type: varchar, size: 255, required: true } announce: { type: varchar, size: 255, required: true } body: { type: longvarchar, required: true }
Листинг 2: метод preloadObjects для объекта News
<?php /** * * @author Igor Brovchenko webdev [at] tigor [dot] com [dot] ua * @package lib.model.news */ class NewsPeer extends BaseNewsPeer { /** * Preload Objects 1.0 * * @param string $class * @param array $objects * @param boolean $isI18n */ static function preloadObjects($class, &amp;$objects, $isI18n = true) { if(!$objects) { return; } $peer = $class . 'Peer'; $setMethod = 'set' . $class; $getMethod = 'get' . $class; if($isI18n) { $resultObjects = call_user_func(array($peer, 'doSelectWithI18n'), new Criteria()); } else { $resultObjects = call_user_func(array($peer, 'doSelect'), new Criteria()); } // array of primary keys $pks = array(); // Remember primary key foreach($resultObjects as $key => $value) { $pks[$value->getPrimaryKey()] = $key; } foreach($objects as &amp;$item) { $preloadObjectId = call_user_func(array($item, 'get' . $class . 'Id')); call_user_func(array($item, $setMethod), $resultObjects[$pks[$preloadObjectId]] ); } unset($resultObjects); unset($pks); } } // NewsPeer
Листинг 3: использование метода preloadObjects
$c = new Criteria(); $c->add(NewsI18nPeer::CULTURE, 'ru'); $news = NewsPeer::doSelectWithI18n($c); NewsPeer::preloadObjects('NewsType', $news, true); foreach($news as $value) { print $value->getId() . ' - ' .$value->getTitle() . ' : ' . $value->getNewsType()->getTitle() . "<br>"; }
Данный способ позволяет подгружать любые объекты с учетом локализации (i18n). В принципе ничего не мешает вынести этот метод из модели и использовать отдельно, например в хелпере. Один недостаток — это что выбираются все значения для объекта NewsType, т.е. если таблица маленькая, то проблем никаких нет. Можно сделать запрос, с условием выборки Criteria IN перечислив все getPrimaryKey.
Используемые материалы:
• Symfony plugin sfPropelActAsTaggableBehaviorPlugin
Источник: Symfony: Propel предзагрузка объектов (preload objects)
Вы можете оставить комментарий или подписаться на RSS feed
Коментарии
// Begin Comments & Trackbacks ?>Разве это хорошо, когда для НОРМАЛЬНОЙ работы скрипта требуется вводить вот такие костыли? Я пока не могу работать с пропелом постоянно, регулярно getConnection и query().
А кто мешает просто написать запрос с аутер джойнами, получить recordset с помощью doSelectRS() и самому разложить в нужные объекты? Делается это гораздо проще
Мдя, помоему проблема symfony в большом количестве запросов к Бд в таких задачах