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 &#91;at&#93; tigor &#91;dot&#93; com &#91;dot&#93; 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;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;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

Коментарии

Мдя, помоему проблема symfony в большом количестве запросов к Бд в таких задачах

Разве это хорошо, когда для НОРМАЛЬНОЙ работы скрипта требуется вводить вот такие костыли? Я пока не могу работать с пропелом постоянно, регулярно getConnection и query().

А кто мешает просто написать запрос с аутер джойнами, получить recordset с помощью doSelectRS() и самому разложить в нужные объекты? Делается это гораздо проще

Leave a comment

(обязательно)

(обязательно)