Как я подружил «memcache» и Propel в Symfony

Данная статья написана в продолжении поста «ORM – зло или Как я пытался кэшировать Propel в Symfony» по наводки пользователя remal.

Как я уже писал в прошлой статье, чтобы заработал кэш необходимо переопределить 4 метода пула, которые находятся в Peer объектах:

public static function addInstanceToPool($obj, $key = null);
public static function removeInstanceFromPool($value);
public static function getInstanceFromPool($key);
public static function clearInstancePool();

* This source code was highlighted with Source Code Highlighter.


Чтобы не дублировать код для каждого Peer объекта (по наводке пользователя remal), можно воспользоваться генератором кода Propel'а. При создании проекта у вас должен создаваться файл config/propel.ini, в котором указаны классы отвечающие за генерацию:

propel.builder.peer.class = plugins.sfPropelPlugin.lib.builder.SfPeerBuilder
propel.builder.object.class = plugins.sfPropelPlugin.lib.builder.SfObjectBuilder

* This source code was highlighted with Source Code Highlighter.


В нашем случае это классы SfPeerBuilder и SfObjectBuilder, которые находятся в плагине sfPropelPlugin.

При изучении этих классов оказалось, что в них есть набор методов, которые вызываются в определенном порядке. В каждый из них передается ссылка на переменную $script. Именуются эти методы по маске add<название_метода_в_сгенерированном_объекте>. Например, addAddInstanceToPool(&$script).

Итак, создаем класс SfPeerBuilderMemcache, где и переопределяем логику SfPeerBuilder

class SfPeerBuilderMemcache extends SfPeerBuilder
{
  protected function addGetPropelCacheStorage(&$script){}
  protected function addRemoveInstanceFromPool(&$script){}
  protected function addClearInstancePool(&$script){}
  protected function addGetInstanceFromPool(&$script){}
}

* This source code was highlighted with Source Code Highlighter.


Но так как кэшировать всю модель нам не надо, то сделаем возможным включать и отключать кэш в схеме (schema.yml). Пример:

user:
  _attributes:  { phpName: User, cache: on }

* This source code was highlighted with Source Code Highlighter.


И добавим в класс SfPeerBuilderMemcache метод, который будет проверять включен или нет кэш для конкретной модели.

protected function isPropelCacheEnabled()
{
  return (boolean)$this->getTable()->getAttribute("cache");
}

* This source code was highlighted with Source Code Highlighter.


Дальнейшую реализацию этого класса оставляю на откуп тем, кому это понадобится… ну или посмотрите исходники в конце статьи :)

Но это еще не все!!! Как я писал в предыдущей статье в случае, когда не используется join'ы при получении связанного объекта Propel вместо retriveByPk() делает запрос в базу, что в нашем случае непозволимо. Чтобы решить эту ситуацию необходимо переопределить класс SfObjectBuilder, а именно метод addFKAccessor, который в случае если связаная модель кэшируется, будет генерировать нужную нам логику:

class SfObjectBuilderMemcache extends SfObjectBuilder
{
 protected function addFKAccessor(&$script, ForeignKey $fk)
 {
  if(!$this->getForeignTable($fk)->getAttribute("cache"))
  {
   return parent::addFKAccessor($script, $fk);
  }
  
  //что-то делаем
 }
}

* This source code was highlighted with Source Code Highlighter.


Теперь нам осталось поменять параметры в файле config/propel.ini на нечто подобное:

propel.builder.peer.class = plugins.sfPropelMemcachePlugin.lib.builder.SfPeerBuilderMemcache
propel.builder.object.class = plugins.sfPropelMemcachePlugin.lib.builder.SfObjectBuilderMemcache

* This source code was highlighted with Source Code Highlighter.


Все! Осталось только пересобрать модель.

Кому нужно подробнее покопайтесь в исходниках.

Что получилось в моем случае?
Я включил кэш для модели Юзер. Теперь если я у объекта Photo вызову метод getUser(), он вызовет метод UserPeer::retrieveByPK(). Который и возьмет объект из кэша или из базы, сохранив в кэш. По аналогии с Photo поступят все объекты связанные с User.

UPD: Продолжение
habrahabr.ru/blogs/symfony/76162/
+12
10 ноября 2009, 21:42
20
frantic 0,0

комментарии (11)

0
VolCh #
Достойное продолжение, для remal тоже плюсик :)
+1
ish #
Актуальность кэша каким образом обеспечивается, можете рассказать?
0
frantic #
Каждый раз когда вызывается метод save у propel объекта, обновляется и кэш (addInstanceToPool($this)).

0
frantic #
Предыдущий коммент отправился не вовремя ). Опишу подробнее.

$oUser->save(); Обновит кэш вызовом метода (addInstanceToPool($this)).
$oUser->delete(); Удалит из кэша объект (removeInstanceFromPool()).
UserPeer::retrieveByPk(); Положит объект в кэш, если его там нет (getInstanceFromPool, addInstanceToPool())
UserPeer::doDelete(); очистит весь кэш модели, если в него передадут объект Criteria (clearInstancePool()) или конкретный объект (removeInstanceFromPool()), если передадут PK.

Таким образом, если мы будем менять данные в базе только через модель, за актуальность отвечает Propel, иначе — мы сами.
0
galk_in #
Как раз изучаю symfony.
Почитал и статью и то с чего начиналось, возник вопрос.
Стоит ли вместо Propel юзать Doctrine?
0
stfalcon #
тоже этот вопрос интересует. для зф юзаю Doctrine — не нарадуюсь. а в Symfony есть встроенная поддержа Доктрины и вдруг Propel…
0
dizzy2 #
А всё просто на самом деле: на текущий момент ORM по-умолчанию в symfony — Propel. Doctrine заменит Propel в версии 1.3, которая должна выйти 30 ноября.
0
develop7 #
Да, сто́ит.
НЛО прилетело и опубликовало эту надпись здесь
0
develop7 #
Потому что кэширование моделей отдано на откуп ORMам. И если Doctrine умеет кэшировать и запросы, и результаты, а Propel — нет, то это исключительно проблемы юзеров Propel.
НЛО прилетело и опубликовало эту надпись здесь

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.