Задать вопрос
tyzhnenko
@tyzhnenko
System Administrator, DevOps, QA Engineer

Кеширование данных "правильнее" описывать в модели или контроллере?

В любом современном проекте стоит сразу предусмотреть кеширование данных, будь то файл, memcached или redis.


Хотелось бы узнать как вы кешируете, т.е. в каком месте, что у вас отвечает за кеширование?


Приведу примеры, например кеширование в контролере, часто встречается в простых примерах на многих сайтах.

Каждый раз когда необходимо получить какую-то информацию из модели, проверяется есть ли данные, которые возвращает метод, в кеше, если нет то «дергаем» модель и сохраняем кеш. Ниже привел примерный код.

class Users extends ApplicationModel {

    function __construct()
    {
    }

    function getName($id) {
        $result = $this->db->execute("SELECT user_name FROM user WHERE id = {$id}");
        return $result;
    }
}

class UserSettings extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showMainSettings() 
    {
        $key = "UserName_{$id}";
        $username = $this->cache->load($key);
        if ($username === false) {
            $username = $user->getName($id);
            $this->cache->save($username, $key);
        }
        /*
            another code and render view
        */
    }

}

class UserHome extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showHome() 
    {
        $key = "UserName_{$id}";
        $username = $this->cache->load($key);
        if ($username === false) {
            $username = $user->getName($id);
            $this->cache->save($username, $key);
        }
        /*
            another code and render view
        */
    }
}



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

Приблизительно тот же код будет выглядеть так:

class Users extends ApplicationModel {

    function __construct()
    {
        $this->cache = new Cache();
    }

    function getName($id) {
        $key = "Users_getName_{$id}";
        $result = $this->cache->load($key);
        if ($result === false) {
            $result = $this->db->execute("SELECT user_name FROM user WHERE id = {$id}");
            $this->cache->save($result, $key);
        }
        return $result;
    }
}

class UserSettings extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showMainSettings() 
    {
        $username = $user->getName($id);
        /*
            another code and render view
        */
    }
}

class UserHome extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showHome() 
    {
        $username = $user->getName($id);
        /*
            another code and render view
        */
    }
}



И еще один пример который встретил, это использование волшебного метода __call(). В таком случае, все методы пишутся так как будто кеширование не используется. В контроллере можно будет использоваться либо прямой вызов метода $object->method() чтобы гарантированно получить информацию из БД. Также можно будет использовать префикс, что-то вроде $object->cache_method(), в этом случае выполнение метода «обворачивается» в кеш. Ниже еще чуть-чуть кода демонстрирующего данный подход.

class Users extends ApplicationModel {

    function __construct()
    {
        $this->cache = new Cache();
    }

    function getName($id) {
        $result = $this->db->execute("SELECT user_name FROM user WHERE id = {$id}");
        return $result;
    }

    function __call($name,$arguments)
    {
    $parts = explode('__', $name);

    if ('cache'==$parts[0] && method_exists($this,$parts[1]) ) {
        $model = get_class($this);
        $method = $parts[1];
        $arguments_str = md5(serialize($arguments));
        $key = "{$model}_{$method}_{$arguments_str}";
        $result = $this->cache->load($key);
        if($result === false)
        {
            $result = call_user_func_array(array($this,"{$method}"), $arguments);
            $this->cache->save($result,$key);
            return $result;
        }
        else 
            return $result;
    }
    else
    {
        echo "Sorry method not found {$model}::{$method}";
        exit(1);
    }
    }    
}

class UserSettings extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showMainSettings() 
    {
        $username = $user->getName($id); // get data from DB
        $username_cached = $user->cache__getName($id); // get cached data if exist
        /*
            another code and render view
        */
    }
}

class UserHome extends ApplicationController {

    function __construct()
    {
        $user = new Users();
    }
    
    function showHome() 
    {
        $username = $user->getName($id); // get data from DB
        $username_cached = $user->cache__getName($id); // get cached data if exist
        /*
            another code and render view
        */
    }
}



Привел три самых распространенных способа которые нашлись в сети. Буду признателен за ссылки на другие да и вообще более продвинутое описание кеширования в/для MVC.


Итак собственно вопрос. Используете ли вы какой-то из этих способов или их модификации и почему именно его? Возможно вы используете что-то более рациональное с точки зрения MVC, поделитесь пожалуйста.


Какой из этих методов или любой другой имеет большее отношение к идеологии MVC?


Заблаговременно спасибо всем тем кто делиться опытом с общественностью!
  • Вопрос задан
  • 5920 просмотров
Подписаться 12 Оценить 1 комментарий
Пригласить эксперта
Ответы на вопрос 6
LeoCcoder
@LeoCcoder
я обычно кеширую в моделе, в контроллере должна быть только функция model->getData, а откуда эти данные берутся это уже не забота контроллера и вот почему: только модель знает какие данные можно/нельзя кешировать, как долго кеш остается валидным и когда и как нужно обновить данные. Плохо когда инфомарция о природе данные размывается и на модель и на контроллер. ИМХО.
Ответ написан
wscms
@wscms
В модели

Модель выбирает данные из базы, сохраняет их в кеш
Модель сохраняет данные в базу — обновляет/очищает кеш

Модель может вызываться из многих контроллеров. В каждом из контроллеров писать сохранение/обновление кеша нерационально.
Ответ написан
В репозитории. Модель простой POPO, о БД вообще ничего не знает, её единственная область ответственности — моделировать предметную область. Ответственность репозитория — хранить сущности модели, где и как только он знает. Как вариант, да, прокси, реализующий тот же интерфейс, что и «чистый» репозиторий. Другой вариант — кэшировать результаты запросов ещё ниже, где-то на уровне DBAL/DAL. Но на практике только прямо в репозитории реализовывал, что-то вроде

class UserRepository {
  public function getById($id) {
    if ($this->cache->hasKey('user' . $id)) {
      $user = $this->cache->getByKey('user' . $id);
    } else {
      $user = $this->db->getUserById($id);
      $this->cache->set('user' . $id, $user);
    }
}
Ответ написан
Комментировать
@egorinsk
Волшебные методы — зло. Человек, которому придется отлаживать ваш код, потом вас заживо на кладбище закопает.

Стоит ли кешировать простые выборки (по id) — спорный вопрос, для ненагруженных проектов — не стоит. Кешировать стоит сложные выборки, и в некоторых случаях, куски HTML.
Ответ написан
delmot
@delmot
Никогда не нравились громоздкие конструкции проверки наличия закешированных данных. Поэтому с радостью использую подход, реализованный в Laravel: laravel.com/docs/cache/usage
Достигнуто избавление от громоздкой конструкции, приведенной в ваших примерах, и при этом сохранена краткость и понятность выполняемых действий.
Ваш вопрос скорее идеологический, восходящий корнями к вопросу «толстые/тонкие контроллеры/модели». Вот пост по теме у Макарова: rmcreative.ru/blog/post/tonkie-i-tolstye-modeli
Ответ написан
akalend
@akalend
программирую
ИМХО сильно зависит от логики, кеширую:
— данные (Вывод в Модели), как правило основного контента, например большой запрос, который бьется на страницы
— кеширую HTML блок (В контроллере), обычно топы, или тематические банеры по тематическим разделам или прочие предложения, не основной контент, но часто запрашиваемый ) Как правило такой контент расчитывается по крону.
— полностью HTML страницу (как правило index.htm или часто-запрашиваемых страниц). Данное кеширование у меня настроено средствами WEB сервера

При желании и кеширование блоков можно настроить средствами WEB сервера, используя ssi. Я писал об этом на Хабре habrahabr.ru/post/109050/
Но в этом случае фреймворк значительно усложняется
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы