Pull to refresh

Comments 66

Вообще с точки зрения MVC получается правильно: View посредством run просит отдать ему данные.
Интересный пример, над которым стоит подумать. Правда субъективно, хочется что-то более элегантное.
Спасибо за решение :)
Рад, что понравилось. Насчет элегантности - две строчки ведь, куда уж элегантнее?
Да я давно уже задаюсь вопросом фрагментарного кэширования в MVC. Философский камень ищу, блин :)
настоящая элегантность - это кеширование должно быть вообще прозрачным и для view и для контроллера, а настраиваться где-нибудь отдельно
Это, между прочим, вопрос - что лучше? Настраивать отдельно (и помнить об этой новой связи) или выполнять действия именно там, где они нужны.
Потом, во view придется все-таки указать сам блок для кэширования, это и сделано.
Заметка все равно не об этом, а только о маленьком нюансе.
Спасибо, вроде все чисто. Надо бы перечитать как-нибудь, а то пока голова не варит :-)
UFO landed and left these words here
Вот такую бы штуку еще на PHP, и половина сайтов заработала бы быстрее! :) А пока придется кешировать фрагмментарно :(
Напишите его на php, велика проблема.
Ну вообще-то проблема. Решаемая, конечно, но геморная. Ведь надо знать, менялись ли данные в базе. Можно такой мониторинг организовать, но гораздо удобнее, проще пользоваться готовым кешером, который будет работать на уроване СУБД. И новичкам будет проще. И все сайты, в которых нет кеширования, заработают немного быстрее. Мелочь, а приятно :)
>> Решаемая, конечно, но геморная. Ведь надо знать, менялись
>> ли данные в базе. Можно такой мониторинг организовать,
не могли бы вы порекомендовать пути решения? тоже бьюсь над проблемой "как узнать, expired ли кэш query, не обращаясь к БД" и кроме как "сбрасывать кэш при изменении данных в админке" (вот это действительно ugly :), ничего в голову не приходит
Варианты есть разные. Вариант 1: поставить триггеры на изменение и добавление данных. При изменении устанавливать флаг в отдельной таблице. Способ наверное самый простой. Проверку флага можно организовать в хранимой процедуре, которая будет возвращать либо кеш, либо выполнит запрос и вернет новые данные, закешировав их. Если кеш не хранится в базе, достаточно просто получать из базы флаг.

Второй вариант очевиден. Управлять флагом при внесении изменений в базу из модели данных. Т.е. когда PHP/Python/Ruby/C# или что-то там еще отправило данные в таблицу, выставлять флаг (можно в базе, можно вне базы).

А можно и вообще без флагов. При добавлении/изменении данных создавать кеш. Это подойдет, если кеш создается относительно быстро, а добавление/изменение происходит относительно редко.

Ну есть вообщем-то и другие варианты, но их я пожалуй опущу...
Часто запросами дело не ограничивается. Вдобавок, результат запроса-то все же достается из кэша и занимает память просто так, не используясь.
А если данные грепаются не из базы? А, скажем, из сокета?
Меньше-кода, больше-дела, вот это хорошо, спасибо, хорошая статья.
Спасибо за возможность.
Хотите элегантное решение? Оно есть в symfony. Использование компонентов. Мы просто берем и весь фрагмент указываем как компонент. Компонент это такой мини-контроллер, который сам по себе не запускается, а лишь выдает фрагмент, который встраивается на страницу. Кешировать его можно просто как и обычные фрагменты шаблонов.

Короче, нефиг придумывать велосипеды :)
+ из этитх реализаций мне больше всего нравится реализация через модель. Она самая простая.
В Рельсах от компонентов отказались. Видимо, тому были причины?
UFO landed and left these words here
'news_detail', :collection => @news/News.latest/вставить_нужное %>
В рельсах очень обоснованно отказать от компанент потому что они стали попросту не нужны.

<%= render :partial => 'news_detail', :collection => @news/News.latest/вставить_нужное %>
В моем фреймворке компоненты тоже есть, конечно. И тем не менее, описанная схема полезна. Впрочем, тут не поспоришь - если не возникает необходимости, то пользоваться и не надо. :-)
Написал апдейт, пояснил.
UFO landed and left these words here
UFO landed and left these words here
В django так, кстати, теги устроены.
Да, такое у меня есть, именно вызывается отдельный метод какого-то контроллера. Однако на практике так делать удобно не всегда.
http://zendframework.com/manual/ru/zend.…

есть такой "фронт энд" Zend_Cache_Frontend_Output
+есть еще и другие "фронт энды"
+в Zend_Cache есть еще понятие бэк эндов, т.е. способов хранения кэша например файл, Memcached, sqlite...
+теги к записям, позволяют гибко управлять очисткой кеша.
Я тут писал не про само кэширование, оно-то у меня именно с помощью Zend Cache и сделано. cacher - это мой класс-обертка, для компактности.
Да, каюсь, прочитал заголовок только и сразу комментировать... =)
UFO landed and left these words here
На мой взгляд, кэширование SQL запросов как в Рельсе 2, не всегда спасет.

Ведь изменение в БД может производить не только приложение на рельсе, которое управляет и кэшем - к БД может подключаться и стороннее приложение, которое будет вносить изменения в БД, а рельса не будет об этом знать.
Вышеописанное касается только кэша, который очищается при изменении данных в БД. Кэш с expires должен при этой схеме работать нормально.
UFO landed and left these words here
а почему бы не разбить шаблон на блоки, где каждый блок будет иметь контроллер и шаблон? тогда кешировать можно на уровне блоков.
Так и делаю, написал выше. Иногда все-таки не все удается разбить, или это породило бы массу дублирования. Пример с последними товарами выбран для простоты. Показательно, что автор подкаста столкнулся именно с проблемой, для которой я предложил решение. То есть это не только мне нужно. ;-)
Написал апдейт, пояснил.
UFO landed and left these words here
Насколько я понял основная фишка здесь паттерн Lazy load (или как-то похоже называется). То есть мы в шаблон передаем не данные, а указываем как их получить. А получаются они только в момент реального использования, то есть при работе с темплейтом.
Может я зануда и эстет, НО:
- вызывать контроллер из представления логически абсурдно
- пихать в контроллер "модельную" состовляющую - некрасиво

p.s. брызь читать внимательно: http://ru.wikipedia.org/wiki/Model-view-controller
И если уж такая пьянка, с вызовом данных из представления, то вместо этого порно - "{block action="Mod_Products.blockNewProducts" limit="10"}", можно было бы сделать так: {model="NewsBlock" view="News"}, т.е. указать какую модель данных выполнить и какое представление наложить на всю эту кашу
UFO landed and left these words here
Так никто же не вызывает ничего подобного. Код читали?
Кроме MVC push существует еще MVC pull, который и подразумевает вызов контроллера из вью.
>> вызывать контроллер из представления логически абсурдно

почитайте, пожалуйста, Фаулера "Архитектура корпоративных программных приложений", ставшую уже классической.

Станете еще большим занудой и эстетом, но в более правильном направлении ;)
Да, это очень похоже, но не совсем то.
В документации: $res = $cache->foobar('1', '2');, то есть для выполнения закэшированного нужно все-таки знать, ЧТО должно выполниться.
У меня же этого знать не нужно. Знаем мы только в контроллере, а в шаблоне всегда одно и то же: $res = utils::run( $res );
Повторюсь: я и так использую Zend Cache, обернув его своим cacher.
function test()
{
if(!$content = $this->CI->cache->load('top_news', 60*60))
{
$this->CI->view(shablon) - К примеру

$this->CI->cache->save($content, 'top_news');
}
return $content;
}

Получаем нужный результат, все происходит до выполенения View.

Класс за основу брал отсюда: http://larin.in/archives/11
Да не то это, это как бы компонент кэшируется целиком.
У меня про другое совсем, см. в апдейте.
>>Класс за основу брал отсюда: http://larin.in/archives/1
:)) наверное все-таки отсюда: http://larin.in/archives/21
Фундаментальная проблема - это то, что контроллер никак не может добраться до этого блока, чтобы "контролировать" его.
Мой концепт (action-controller)

...
$this->page()->getLayout()->getLayout()->getBlock('spisokTovarov)->аВотТутМожноВсе();
...
или:
$this->page()->getLayout(2)->getBlock('spisokTovarov)->аВотТутМожноВсе();
ошибся немного - page - это свойство:
$this->page->getLayout(2)->getBlock('spisokTovarov)->аВотТутМожноВсе();
(ну да ладно:)
Очень полезная идея, эдакий отложенный запуск экшна. Правда есть подозрение, что если подумать, то можно реализовать это элегантнее.

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

А вообще, спасибо за идею.
Спасибо за подсказку про строку, проверю, не знал.
Ну, собственно, да - элегантнее можно. По крайней мере в том движке, в котором работаю я, все данные к представлению попадают через специальный объект, который называется "контекст" (в шаблоне он называется $c), так вот:

Контроллер (было):

$this->c->someVar = $this->calculateSomeVar( $argument );

Контроллер (стало):

$this->c->jitVar( 'someVar', $this, 'calculateSomeVar', $argument );


Представление (не изменяется):

<?= $c->someVar ?>


Как написать __get() надо объяснять?
Ну что ж, действительно, при таком подходе (похоже на Code Igniter, что ли) так будет совсем красиво. У меня-то переменные в шаблоне уже просто как $product, $category, $books и т.п. появляются, так что __call или __get некуда приделывать.
Замечательное развитие мысли, спасибо.
все круто но само по себе кеширование кусков кода или результатов функций это просто

сложнее гораздо когда один и тот же кусок данных используется в нескольких местах) в этом случае при его изменении необходимо сбрасывать кеш со всеми местами где он используется) вот в таком случае не просто получается
да и вообще КЭШИРОВАТЬ НУЖНО НЕ КУСКИ VIEW а куски данных которые нужны для вью, которые собираются контроллером, кэширование вью это отдельная ступенька
У меня, например, сделано так: кэшируются выборки из базы. Каждая выборка- отдельный объект, который может быть поименован согласно того что именно выбирается (это делается как правило на уровне модели), т.е. контроллер спрашивает у модели "записи пользователя 1", модель возвращает выборку и именует ее. Именование идет каскадно. Соотв. по имени можно сбросить все связанные кэши.

Если очень хочеться закэшировать html, то у меня это реализовано приблизительно следующим образом:
1. Выборка передается в шаблон, шаблон пробегается по ней в цикле, при этом непосредственно запрос к БД/кэшу происходит on-demand.
2. Если мы кэшируем какой-то участок шаблона- если кэш не найден, запоминаются все имена произведенных выборок, после чего запоминаем в кэше собствено html и связи с именованными выборками.
3. При изменении модели она сбрасывает все кэши с некоторым именем, как кэши выборок, так и непосредсвенно html.

Сам вызов кэша в шаблоне реализован как активный тег поэтому выглядит все как-то так:
контроллер

$sel= new items_select();
$sel->Where('UserID',eq,$UserID);
//Имя кэша:
$sel->setCacheName('user_items_'.$UserID);
//Постраничный вывод:
$sel->Limit((int)$_REQUEST['page']*10,10);

Шаблон:
(тупо делаем имя по строке запроса)

<!-- op:cache="user_items_{$_SERVER.REQUEST_URI}" lifetime="1000"-->
<!--op:each="$sel"-->
<a href="{$Link}">{$Title}</a>
<!--/op:each-->
<!--/op:cache-->
Написал подробную статью о том как устроены шаблоны Django и как в Django решается сабж.
Sign up to leave a comment.

Articles