Да уж … прошло уже более 2х месяцев со дня последнего поста. Честно признаться - времени не хватает, и работы много, и семья, и хобби.

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

Проблема №1. Рандом

Рандом в битриксе существует в разных проявлениях. Одну из рандомных проблем разработчики битрикса решили - это рандомные строки. Часто разработчики используют рандомные строки в своих компонентах и шаблонах для того, чтобы создавать какие-то уникальные идентификаторы для каких-то элементов. Например, нужно вывести на странице несколько блоков с изображениями или видео, а в последствии обратиться к ним через js по идентификатору. Сгенерировать случайную строку можно разными способами в php, в битриксе есть специальная функция для генерации случайных строк и величин.

Тут и кроется первая проблема. Если компонент на каждом вызове будет генерировать какую-то случайную строку и будет выводить ее в шаблон, то этот шаблон в композитном режиме работать не будет, просто потому, что он меняется каждый хит.

Решение достаточно простое, оно описано в официальном курсе битрикса по композитному сайту. Для CBitrixComponent написали метод randString(), который генерирует псевдо-случайную величину, которая будет зависеть строго от композитного кеша.

Проблема №2. Снова рандом

Простая и очень частая ситуация - часто на главной странице интернет-магазина выводится какой-то блок (Спецпредложения, Новинки, Самые продаваемые товары), который содержит в себе какой-то набор товаров. Часто товары в таких блоках выводятся в случайном порядке. Ясно дело - раз товары меняются на каждом хите, то и композитный кеш для страницы нормально работать не будет.

Тут уже придется немного поломать голову над решением. Первое что приходит в голову - подгружать данный блок на страницу с помощью ajax уже после загрузки. Ну и спрашивается - зачем тогда композит? Кого устраивает - можно сделать и так, но тогда кто-то может не увидеть этот контент (например злобный SEO-шник, который думает, что поисковики до сих пор анализируют сайты по загруженному контенту без учета отработки ajax запросов после загрузки страницы). Мне такой вариант не подошел.

Идем дальше. Можно рендерить с помощью js! Для меня это тоже не вариант в большинстве случаев. Загружать на страницу каждый раз придется больше данных, чем требуется. Дополнительно придется писать рендер на js с помощью какого-нибудь очередного новомодного фреймворка типа angularjs, ну или на коленках. Как-то все это не унифицированно …

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

Ну и наконец путь, которым пошел я, как обычно, через ж … Пришлось опять лезть в ядро. Оказывается, можно научить сайт использовать композит не только в компонентах, но и в любом другом месте. Для этого можно использовать класс \Bitrix\Main\Page\FrameStatic. Простой пример:

//Создаем экземпляр класса. В качестве параметра используем уникальный идентификатор, которым помечен композитный блок в верстке. Вам нужно создать блок и указать для него id самостоятельно
$bestProductsFrame = new \Bitrix\Main\Page\FrameStatic('main-random-best-products-composite');

//Начинаем буферизацию нашего динамического рандомного блока
$bestProductsFrame->startDynamicArea();

//Вызываем наш компонент, который генерирует рандомный набор товаров
$APPLICATION->IncludeComponent();

//Устанавливаем заглушку, которая будет показана на первом хите композита
$bestProductsFrame->setStub('Загрузка');

//Завершаем буферизацию
$bestProductsFrame->finishDynamicArea();

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

В результате работы этого кода посетитель, пришедший на сайт, увидит на мгновение сначала один набор товаров (условно рандомный), и мгновенно тут же этот html будет подменен на реально рандомный html со списком товаров. На эту смену контента можно еще наложить и какой-то эффект, чтобы обновление контента смотрелось гладко.

Прошу, напишите в комментах, если у вас есть более оригинальное, продуманное и взвешенное решение. Мне, увы, в голову больше ничего не пришло

Проблема №3. События

Было дело - подключал композит на сайте, на котором использовались региональные поддомены (которые сам и реализовывал). Так получилось, что функционал, связанный с региональностью, был завязан на обработчике события OnPageStart (определение региона, редирект на нужный поддомен и т.д.)
Как только подключили композит, начались странные глюки. Редирект, который должен был срабатывать при наличии определенной куки у пользователя, не срабатывал. Опытным путем было выяснено, что он таки срабатывает, но только лишь на втором композитном хите, а первый хит не доходит до редиректа.

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

К сожалению, решить данную проблему без вмешательства в ядро мне так и не удалось. Сделал просто - добавил подключение своего скрипта до подключения непосредственно композита, например в файле /bitrix/header.php

require_once(__DIR__ . '/../local/compositeRedirector.php');
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog.php");

А уже в подключенном файле - вся необходимая мне логика.
Кстати, на эту тему создал идею для разработчиков битрикса, поддержите плиз 🙂

Заключение

В общем-то битрикс не придумал ничего нового - просто сделали еще одну оболочку поверх существующего кеша. Подобный кеш уже давно имеется и в других системах управления сайтами, подобие подобного кеша есть даже в phpbb.

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