Довольно часто стоит такая задача, чтобы у компонента шаблон кэшировался, но некоторые части были динамичными. Допустим, компонент для построения страницы товара. [spoiler]На нем есть блоки: цена, количество данного товара в корзине, добавлен ли товар в сравнение, добавлен ли товар в wishlist, да мало ли что еще может быть. Все эти блоки естественно индивидуальны для конкретного пользователя, а значит кэшировать весь шаблон нельзя - либо все пользователи будут видеть состояние какого-то одного пользователя на момент кэширования, либо кэширование не будет вообще работать. И решил я решить эту проблему на одном интернет-магазине и решение хотелось красивое. Вроде как получилась красота. Итак.
Сразу определим новый синтаксис для шаблона компонента. В шаблоне появляются как бы подшаблоны - они будут включаться в шаблон во время генерации. При этом они не кэшируются, но имеют доступ ко всем переменным шаблона типа $arResult, $arParams. Самое прикольное, что включаться они будут произвольно путем указания специальной конструкции. Теперь конкретно пример файла шаблона (template.php) для компонента:
#USER_CART# - это место для включения подшаблона. При этом USER_CART - это название файла, т.е. сюда подключим user_cart.php - этот файл находится там же, где и основной шаблон компонента template.php . Этот файл может быть принципиально любым, обычный шаблон компонента. Ну допустим:
Ниже привожу код простейшего компонента с дополнительной обработкой:
В коде пустыми строками разделены основные логические блоки. Первый блок - собственно работа компонента, но она буферизируется в переменную $template_data. Т.е. там находится результат отработки компонента + шаблона template.php, который у нас еще содержит вставки вида #FILE#.
Во втором блоке мы эти вставки меняем, для этого используем preg_match_all, дергаем коды, подключаем отдельные подшаблоны с помощью $this->IncludeComponentTemplate(). Коды в template.php заменяются на подшаблоны. Ну и собственно показываем результат работы компонента.
Итог работы данной байды при включенном автокэшировании будет следующим. Первый запрос:
Но вот следующий запрос через пару секунд выдаст уже:
Как видно - первая строка закэшировалась и не меняется, а вторая не кэшируется.
Естественно $arResult будет закэширован всегда и для подшаблонов. Поэтому в подшаблонах необходима своя обработка данных. Но в любом случае эта обработка данных будет существенно меньше, чем перегенерация всего компонента и обычно хватает проверки внешних данных.
Где я это сейчас использую. Компонент для отображения страницы товара, показывает обычную страницу товара, но на ней отражается пользовательская цена (скидки для пользователя, показ цены в зависимости от выбранной валюты), количество данного товара в корзине, выбран ли товар для сравнения. Каждая эта область построена на подшаблонах и только для цены производятся какие-то обращения к БД, остальные - просто проверка переменных и показ данных по ситуации. Допустим, состоит ли товар в сравнении - показывает checkbox с галочкой или без.
Сразу определим новый синтаксис для шаблона компонента. В шаблоне появляются как бы подшаблоны - они будут включаться в шаблон во время генерации. При этом они не кэшируются, но имеют доступ ко всем переменным шаблона типа $arResult, $arParams. Самое прикольное, что включаться они будут произвольно путем указания специальной конструкции. Теперь конкретно пример файла шаблона (template.php) для компонента:
<div>Товар № <?=$arResult['ID']?></div> <div>#USER_CART#</div> |
#USER_CART# - это место для включения подшаблона. При этом USER_CART - это название файла, т.е. сюда подключим user_cart.php - этот файл находится там же, где и основной шаблон компонента template.php . Этот файл может быть принципиально любым, обычный шаблон компонента. Ну допустим:
<strong><?=time()?></strong> |
Ниже привожу код простейшего компонента с дополнительной обработкой:
ob_start(); // буферизуем весь вывод компонента, чтобы потом вставить переменные if ($this->StartResultCache(false, $cache_id)) { $arResult['ID'] = time(); $this->IncludeComponentTemplate(); } $template_data = ob_get_clean(); preg_match_all('/\#(\w+)\#/e', $template_data, $matches); if (is_array($matches)) { $keys = $matches[0]; $vars = $matches[1]; foreach ($keys as $key_index=>$key_name) { ob_start(); $this->IncludeComponentTemplate(strtolower($vars[$key_index])); $mini_template_data = ob_get_clean(); $template_data = str_replace($key_name, $mini_template_data, $template_data); } } print ($template_data); |
В коде пустыми строками разделены основные логические блоки. Первый блок - собственно работа компонента, но она буферизируется в переменную $template_data. Т.е. там находится результат отработки компонента + шаблона template.php, который у нас еще содержит вставки вида #FILE#.
Во втором блоке мы эти вставки меняем, для этого используем preg_match_all, дергаем коды, подключаем отдельные подшаблоны с помощью $this->IncludeComponentTemplate(). Коды в template.php заменяются на подшаблоны. Ну и собственно показываем результат работы компонента.
Итог работы данной байды при включенном автокэшировании будет следующим. Первый запрос:
<div>Товар № 1253136484</div> <div><strong> 1253136484</strong></div> |
Но вот следующий запрос через пару секунд выдаст уже:
<div>Товар № 1253136484</div> <div><strong> 1253136487</strong></div> |
Как видно - первая строка закэшировалась и не меняется, а вторая не кэшируется.
Естественно $arResult будет закэширован всегда и для подшаблонов. Поэтому в подшаблонах необходима своя обработка данных. Но в любом случае эта обработка данных будет существенно меньше, чем перегенерация всего компонента и обычно хватает проверки внешних данных.
Где я это сейчас использую. Компонент для отображения страницы товара, показывает обычную страницу товара, но на ней отражается пользовательская цена (скидки для пользователя, показ цены в зависимости от выбранной валюты), количество данного товара в корзине, выбран ли товар для сравнения. Каждая эта область построена на подшаблонах и только для цены производятся какие-то обращения к БД, остальные - просто проверка переменных и показ данных по ситуации. Допустим, состоит ли товар в сравнении - показывает checkbox с галочкой или без.