Хлебные крошки в 1С-Битрикс

Здравствуйте, уважаемые читатели!
Хочу поделиться с вами опытом решения своей проблемы в нашем всеми любимом CMS 1C- Битрикс.


Задача: Необходимостью разместить хлебные крошки внутри кешируемого шаблона.


Условие 1: Сделать алгоритм отложенного вызова, но не такой, какой предусмотрен системой Битрикс. В уже существующем алгоритме есть проблема с кешем. Следующее включение компонента


$APPLIACATION->IncludeComponent("bitrix:breadcrumb", ".default"); 

включает буферизацию методом AddBufferContent, который в свою очередь вешает событие onEndBufferContent. Это событие обрабатывается после кешированием, поэтому в кеш буферизированные данные попасть не могут. Это приводит к потере части контента. (В моем случае пропадал код шаблона, расположенный до вызова компонента bitrix:breadcrumb.)


Условие 2: Модификация хлебных крошек может производиться после вызова шаблона.


Сама по себе проблема не нова, но нормального решения так и не смог найти. Поэтому было предпринято создать свое.
Решение было найдено — использовать component_epilog.php.
Схема работы такая: В шаблоне делаем placeholder, например ##PLACEHOLDER_1##. Затем в не кешируемом component_epilog.php заменяем его на нужный нам контент.


Создаем класс, например ComponentHelper, со следующим содержимым:


<?php
#/bitrix/php_interface/classes/ComponentHelper.php
namespace PHPInterface;

/**
 * ComponentHelper
 *
 * Создает плейсхолдеры в шаблоне
 * При помощи статической функции handle обрабатывает их
 * Класс необходим для вызова некешируемых функций
 */
class ComponentHelper
{
    private $component = null;
    private $lastPlIndex = 0;
    private $pull = array();

    public function __construct(\CBitrixComponent $component)
    {
        $this->component = $component;
        $this->component->SetResultCacheKeys(array('CACHED_TPL', 'CACHED_TPL_PULL'));
        ob_start();
    }

    public function deferredCall($callback, $args = array())
    {
        $plName = $this->getNextPlaceholder();
        echo $plName;
        $this->pull[$plName] = array('callback' => $callback, 'args' => $args);
    }

    public function saveCache()
    {
        $this->component->arResult['CACHED_TPL'] = @ob_get_contents();
        $this->component->arResult['CACHED_TPL_PULL'] = $this->pull;
        ob_get_clean();
        $this->component = null;
    }

    private function getNextPlaceholder()
    {
        return '##PLACEHOLDER_'.(++$this->lastPlIndex).'##';
    }

    public static function handle(\CBitrixComponent $component)
    {
        $buf = &$component->arResult['CACHED_TPL'];
        foreach ($component->arResult['CACHED_TPL_PULL'] as $plName => $params) {
            list($prevPart, $nextPart) = explode($plName, $buf);
            echo $prevPart;
            call_user_func_array($params['callback'], $params['args']);
            $buf = &$nextPart;
        }
        echo $buf;
    }
}

Особенность работы скрипта в том, что функция handle выводит контент до плейсходера, обрабатывает сам плейсхолдер, затем переходит к следующему. Алгоритм поиска плейсходеров является итеративным.
Соотвественно, чтобы вывести хлебные крошки, понадобится создать функцию, например
ShowNavChain в файле init.php.


function ShowNavChain($template = '.default')
{
    global $APPLICATION;
    $APPLICATION->IncludeComponent('bitrix:breadcrumb', $template);
}

В шаблоне пишем такие строки:


$helper = new PHPInterface\ComponentHelper($component);
$helper->deferredCall('ShowNavChain', array('.default'));
//...
// И в конце шаблона обязательно вызвать 
$helper->saveCache();

Рядом с шаблоном располагаем файл component_epilog.php
и помещаем в него примерно такой код:


PHPInterface\ComponentHelper::handle($this);

Не забываем в /bitrix/php_interface/init.php добавить:


require_once(dirname(__FILE__).'/classes/ComponentHelper.php');
Tags:
битрикс, php

You can't comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author's username will be hidden by an alias.