Как стать автором
Обновить

Кэшируем модели в Zend Framework

Совсем недавно стала задача организовать кэширование данных. И получилась весьма удобная вещь. Реализацию смотри под хабракатом…

Описанный ниже абстрактный класс кладем к нашим моделям в папку Cache. В моём случае это /Default/Models/Cache/Abstract.php

abstract class Default_Models_Cache_Abstract
{
  protected static $_cache = null;
  protected static $_staticCache = null;
  protected static $_tags = null;
  protected static $_className = null;

  protected static $_frontendName = 'Class';
  protected static $_backendName = 'File';
  protected static $_frontendOptions = array();
  protected static $_backendOptions = array();

  public function __construct()
  {
    self::_init();
  }

  public function __call($name, $arguments)
  {
    return self::_call($name, $arguments);
  }

  public static function __callStatic($name, $arguments)
  {
    return self::_call($name, $arguments, true);
  }

  protected static function _initConfig()
  {
    static $inited = null;
    if (null === $inited) {
      if (null === static::$_className) {
        throw new Exception("You must provide a protected static"
          . " \$_className = ...; statement in your class!"
);
      }
      self::$_backendName = 'File';
      self::$_frontendName = 'class';
      self::$_frontendOptions = array();
      self::$_backendOptions = array();
      $inited = true;
    }
  }

  protected static function _init($useStatic = false)
  {
    $ref = null;
    if ($useStatic && null === self::$_staticCache) {
      $ref = static::$_className;
    } elseif (null === self::$_cache) {
      if (!class_exists(static::$_className)) {
        require_once 'Zend/Loader.php';
        Zend_Loader::loadClass(static::$_className);
      }
      $ref = new static::$_className;
    }
    if (null !== $ref) {
      self::_initConfig();
      self::$_tags = array(static::$_className);
      self::$_frontendOptions['cached_entity'] = $ref;
      $cache = Zend_Cache::factory(
        self::$_frontendName,
        self::$_backendName,
        self::$_frontendOptions,
        self::$_backendOptions
      );
      if ($useStatic) {
        self::$_staticCache = $cache;
      } else {
        self::$_cache = $cache;
      }
    }
  }

  protected static function _generateId($methodName, $arguments)
  {
    return sha1($methodName . print_r($arguments, true));
  }

  protected static function _call($name, $arguments, $useStatic = false)
  {
    self::_init($useStatic);
    $id = self::_generateId($name, $arguments);
    if ($useStatic) {
      $cache = self::$_staticCache;
    } else {
      $cache = self::$_cache;
    }
    if (!($result = $cache->load($id))) {
      $result = $cache->__call($name, $arguments);
      $cache->save(
        $result,
        $id,
        self::$_tags
      );
    }
    return $result;
  }

  public function cleanCache()
  {
    $ids = self::$_cache->getIdsMatchingTags();
    foreach($ids as $id) {
      self::$_cache->remove($id);
    }
    self::$_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, self::$_tags);
  }
}


Для его работы необходимо иметь рабочую модель, например Default_Models_Products.
В папке Cache, относительно файла нашей модели, создаем класс Default_Models_Cache_Products с одним свойством:

class Default_Models_Cache_Products extends Default_Models_Cache_Abstract
{
  protected static $_className = 'Default_Models_Products';
}


Все обращения к классу Default_Models_Products заменяем на Default_Models_Cache_Products, работаем с ним также как и с Default_Models_Products.
Например, в нашей моделе реализован метод getList.

class Default_Models_Products
{
  ...

  public function getList()
  {
    $productsTable = new Default_Models_Table_Products;
    return $productsTable->fetchAll()->toArray();
  }


Получить данные мы можем следующим образом:

$productsModel = new Default_Models_Cache_Products();
$productList = $productsModel->getList();


Реализована также возможность вызова статических методов. Если метод getList является статическим, то вызвать его можно будет так:

$productList = Default_Models_Cache_Products::getList();


Очистку кэша можно произвести вызвав cleanCache(), в данном случае будет произведена очистка относящегося только к данной моделе, что может оказаться весьма полезным. Дабы не очищать весь кэш.

Про задание параметров $_frontendOptions и $_backendOptions можно почитать тут и тут

Получившийся класс весьма универсален и подойдет не только для моделей.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.