В оригинале это выглядит примерно так:
Copy Source | Copy HTML
$model = MyModel();
- $model_cached = MyModelCached();
- $values_direct = $model->doStuff();
- $values_cached = $model_cached->doStuff();
Этот путь мне не понравился сразу по нескольким причинам.
Во первых - удваивание количества сущностей. Когда моделей много и Вы используете не по одной модельке на запрос монструозненько получается.
Во вторых - слишком много настроек кеша. Это очень хорошо и к месту, когда логика кеша сложна или нетривиальна. В моём случае весь кеш работает с одними и теми же настройками и совершать лишних телодвижений по его инстанцированию не хотелось бы.
Реализовано кеширование через расширение класса Zend_Db_Table_Abstract и магический метод __call.
Для начала загоним настройки нашего дефолтного кеша в регистр (я это делаю в бутстрапе)
Copy Source | Copy HTML
$backendOptions = array(
- 'servers' => array( array(
- 'host' => '127.0.0.1',
- 'port' => 11211,
- 'persistent' => true,
- 'weight' => 1,
- 'timeout' => 10,
- 'retry_interval' => 15,
- 'status' => true,
- 'failure_callback' => null ) ) );
- Zend_Registry::set('backendCacheOpt', $backendOptions);
Ну и собственно сама прослойка
Copy Source | Copy HTML
/**
* Кеширующая прослойка для zend_db_table
*
* Zend_Registry::get('lifeTimeUP')
* Zend_Registry::get('backendCacheOpt')
*
*
* @desc кэш работает через __call метод
_{method} для получения данных из БД
*/
- abstract class MyCachedDbTable extends Zend_Db_Table_Abstract
- {
- protected $_cached = null;
- public final function init()
- {
- parent::init();
- $this->_setCache();
- }
- protected function _setCache()
- {
- $this->_cached = Zend_Cache::factory(
- 'Core',
- 'Memcached',
- array(
- 'lifetime' => Zend_Registry::get('lifeTimeUP'),
- 'automatic_serialization' => true,
- 'caching' => (APPLICATION_ENV == 'production') ? true : false,
- 'cache_id_prefix' => 'dseyeUP_',
- 'ignore_user_abort' => true ),
- Zend_Registry::get('backendCacheOpt'));
- }
- /*
* проверяет, существует ли метод "_{$method}"
* пробует считать из кеша
* сохраняет в кеш
* кидает исключения
*/- public function __call( $method, $opt)
- {
- $signature = "{$this->_name}_{$method}_" . md5(serialize($opt)); //сигнатура метода по опциям
- $methodDB = "_{$method}"; //имя метода обращения к ДБ
- if( !method_exists($this, $methodDB) )
- throw new Exception("Method {$methodDB} for DB not found! Cache crash =(");
- if( !( $data = $this->_cached->load($signature) ) )
- {
- $data = call_user_func_array(array($this, $methodDB), $opt);
- $this->_cached->save($data, $signature);
- }
- return $data;
- }
- }
Теперь любая отнаследованная от данной прослойки модель может кешировать свои вызовы.
К примеру таблица новостей
Copy Source | Copy HTML
/*
* модель новостей
*/
- class App_Model_DbTable_News extends MyCachedDbTable
- {
- protected $_name = 'news';
- /*
* получить несколько последних новостей
*/- public function _lastOf( $count = 3 )
- {
- $select = $this->select();
- $select->from($this, array( 'id', 'cat', 'title', 'desc', 'date' => 'DATE_FORMAT(`date` , \'%H:%i %d.%m.%Y\')' ))
- ->order('news.date DESC')
- ->limit( (int)$count );
- return $this->fetchAll($select);
- }
- }
Теперь вызов метода lastOf данной модельки приведёт к поиску по кешу и отдаче закешированного результата в случае успеха.
Если необходимо получить свежий результат (тобишь точно не из кеша) - обращайтесь напрямую к методу _lastOf.
Если хотите запретить обращение к методу в обход кеша - сделайте метод _lastOf protected.
Неочевидный суффикс кешируемых методов (нижнее подчёркивание) лучше заменить на чтолибо более очевидное, например noCache_lastOf. У себя я оставил нижнюю черту ради краткости и ввиду того что проект пишу вдвоём =)
Ну и собственно минусы моего решения выходят из его плюсов:
1. Неочевидность логики кеширования без просмотра кода прослойки
2. Жёсткое конфигурирование
Поэтому решение стоит применять с осторожностью.
Комментариев нет:
Отправить комментарий