Цель: уменьшить количество запросов для таблиц использующих много внешних ключей.
Описание проблемы: имеем 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, &$objects, $isI18n = true)
{
if(!$objects)
{
return;
}
$peer = $class . 'Peer';
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 &$item)
{
$preloadObjectId = call_user_func(array($item, 'get' . $class . 'Id'));
call_user_func(array($item, 'set' . $class), $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


