Skip to content

Instantly share code, notes, and snippets.

@if0rest
Last active April 8, 2024 06:12
Show Gist options
  • Save if0rest/dd3850ac45fe7a52f49b08c9d7cbd938 to your computer and use it in GitHub Desktop.
Save if0rest/dd3850ac45fe7a52f49b08c9d7cbd938 to your computer and use it in GitHub Desktop.

Как вывести произвольный контент в шаблоне сайта и компонента

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

  1. Размещаем в нужном месте отложенную функцию. Она поставит #МЕТКУ#, которая позже будет заменена результатом выполнения откладываемого кода.
    # header.php:
    
    <div id="sidebar">
        <?$APPLICATION->ShowViewContent("sidebar")?>
    </div>
  1. Код, выполнение которого необходимо отложить, оборачиваем в конструкцию:
    # template.php:
    
    <?$this->SetViewTarget("sidebar");?>
        <div class="element-filter">
            <!--вывод фильтра-->
        </div>
    <?$this->EndViewTarget();?>
    # component_epilog.php:
    
    <?$this->__template->SetViewTarget('content_id')?>
        <!--here your code-->
    <?$this->__template->EndViewTarget()?>
    
    Если не работает при включенном кэшировании, необходимо выполнить метод InitComponentTemplate():
    
    <?if(!$this->__template) $this->InitComponentTemplate();?>

Методы, доступные в шаблоне (через $this):

  • CBitrixComponentTemplate::SetViewTarget($view, $pos)
  • CBitrixComponentTemplate::EndViewTarget()

Методы глобального объекта $APPLICATION:

  • Cmain::AddViewContent($view, $content, $pos)
  • Cmain::ShowViewContent($view)

где:

  • $view – идентификатор буферизируемой области;
  • $content – буферизируемый контент;
  • $pos – сортировка вывода контента.

Примечание: одному идентификатору $view может соответствовать несколько буферов. Последовательность вывода контента определяется сортировкой $pos.

https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=3855


Выводим компонент в шаблоне другого компонента

Задача

Вывести компонент в шаблоне другого компонента, при условии включенного кэширования Битрикс.

Решение

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

Для решения этой проблемы можно воспользоватья файлом component_epilog.php

  • Добавим метку в шаблон основного компонента (template.php)

    <?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
    $this->setFrameMode(true);
    // начинаем буферизацию вывода
    ob_start();
    // тут разметка и код основного компонента, а в нужном месте вставляем метку:
    
    #INNER_BLOCK_1#
    
    <?
    // передаем данные буфера вывода в файл component_epilog.php
    $this->__component->SetResultCacheKeys(array("CACHED_TPL"));
    $this->__component->arResult["CACHED_TPL"] = @ob_get_contents();
    ob_get_clean();
    ?>
  • Вынесем в файл component_epilog.php основного компонента вызов вложенного компонента и всей нужной разметки

    <? require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/header.php");
    /**
     * @var CMain $APPLICATION
     * @var array $arResult
     */
    
    // callback function
    $replacer = function ($matches) use ($APPLICATION) {
      ob_start();
      // тут вставляем разменту, вызовы компонентов, в общем все что нужно вывести
      // в метке #INNER_BLOCK_123# мы можем передать в качестве числа например код инфоблока
      // и использовать его так :
      $id = $matches[1];
      // например в вызове компонента списка новостей:
      $APPLICATION->IncludeComponent(
        "bitrix:news.list",
        "article-list",
        Array(
          'IBLOCK_ID' => $id,
          // тут миллион параметров компонента
        )
      );
      
      return ob_get_clean();
    };
    // находим метку и заменяем ее на результат работы нашей функции
    echo preg_replace_callback(
      "/#INNER_BLOCK_([\\d]+)#/is" . BX_UTF_PCRE_MODIFIER,
      $replacer,
      $arResult["CACHED_TPL"]
    );

Решение 2

  • В шаблоне первого компонента

    <? $this->setViewTarget('view_marker'); ?>
    тут код шаблона компонента
    <? $this->endViewTarget(); ?>
  • В шаблоне второго компонента

    <?= $APPLICATION->getViewContent('view_marker') ?>

https://gdecider.github.io/articles-bx-inner-component.html


component_epilog.php и кеширование не работают вместе

Когда отрабатывается catalog.section, внутри срабатывает вызов генерации строки навигации. Но так как этот вызов происходит внутри той части кода, которая кэшируется, то при повторном обращении к странице и работе catalog.section из кэша, второй раз вызов не происходит. Следовательно - бесполезно пытаться добавить component_epilog.php к шаблону строки навигации.

Подозреваю, что вы хотите сделать rel=canonical или что-то подобное? Если так, то тут нужно действовать по-другому. После того, как отрабатывает компонент catalog.section, в $arResult появляется поле NAV_RESULT ($arResult["NAV_RESULT"]). Это поле содержит всю информацию о постраничной навигации.

Следовательно вам нужно работать с component_epilog.php компонента catalog.section, а не system.pagenavigation. В эпилоге вы вытаскиваете $arResult["NAV_RESULT"] и, опираясь на него, делаете, что вам нужно. Например, в моём случае имел место кусок кода:

if (!$arResult['NAV_RESULT']["NavShowAll"]) {
    $APPLICATION->AddHeadString('<link rel="canonical" href="' . $arResult['NAV_RESULT']['sUrlPath'] . '?'
        . $strNavQueryString . 'SHOWALL_' . $arResult['NAV_RESULT']['NavNum'] . '=1" >'); 
}

А ещё, может быть кому-то будет полезно, по теме есть другая штука. Я знаю ещё один случай, когда component_epilog.php не отрабатывает - это компонент search.page. Может быть, я глупо пытался использовать его в режиме кэширования, но голову я себе тогда сломал основательно. Оказалось, он использует не стандартный вызов IncludeComponentTemplate, а связку

if ($this->InitComponentTemplate($templatePage)) {
    // ...
    $this->ShowComponentTemplate();
}

Как мне объяснили в техподдержке - это старый вариант вызова шаблона (кстати, он позволяет делать интересные штуки. кому интересно - почитайте код вышеупомянутого компонента). Переделывать на новый (который поддерживает component_epilog.php) не стали, так как использование компонента в режиме кэширования не предполагается. Больше никакой магии с component_epilog.php мне не известно.

https://dev.1c-bitrix.ru/support/forum/messages/forum6/topic58419/message363387/#message363387


Пример работы с SetViewTarget

В ядре продукта 1С-Битрикс имеется замечательный метод CMain::ShowViewContent, который позволяет установить выводимый контент для функции AddViewContent. С помощью этого метода можно вывести компонент, где угодно. Допустим, мы используем комплексный компонент bitrix:catalog, и фильтр у нас будет вызываться в файле шаблона section.php. Тогда вызов фильтра должен быть таким:

if (!isset($_REQUEST["ajax"])) $this->SetViewTarget("sidebar");

$APPLICATION->IncludeComponent(
    "bitrix:catalog.smart.filter",
    "",
    array(...),
    false
);

if(!isset($_REQUEST["ajax"])) $this->EndViewTarget("sidebar");

Т.е. перед фильтром мы вызываем метод SetViewTarget, после EndViewTarget. В результате фильтр отработает ДО компонента bitrix:catalog.section, но показан он не будет. Затем в том месте, где мы хотим отобразить фильтр, необходимо вызвать метод:

$APPLICATION->ShowViewContent('sidebar');

Это может быть любым местом: шаблон сайта, шаблон другого компонента, включаемая область.


Пример для статических файлов

Для задания контента, который должен быть помещен в определенную область с помощью отложенной функции, необходимо воспользоваться конструкцией:

ob_start();
echo 'SomeText';
$content = ob_get_clean();

$APPLICATION->AddViewContent('content', $content); ?>

SetViewTarget из component_epilog

Воспользоваться конструкцией $this->SetViewTarget из файла component_epilog.php не удастся, т.к. в данном файле это уже объект класса CBitrixComponent (а не CBitrixComponentTemplate, как в файле template.php). В таком случае нужно использовать конструкцию вида:

<?$this->__template->SetViewTarget('mdf_title');?>
    same text here ...
<?$this->__template->EndViewTarget();?>

https://pai-bx.com/wiki/1c-bitrix/29-example-of-setviewtarget/


ShowViewContent в bitrix

Чтобы передать данные, например, из шаблона компонента в шаблон сайта, можно воспользоваться функционалом отложенных функций.

ShowViewContent - это обычная отложенная функция, такая же как и SetTitle или SetPageProperty. Поэтому вывод контента с помощью ShowViewContent может быть выше по коду, чем передача самого контента с помощью AddViewContent или с помощью SetViewTarget + EndViewTarget.

Передаём контент в буферизируемую область:

$APPLICATION->AddViewContent('myContentBlockName', '<p>Hello</p>');

И указываем, где нужно выводить содержимое этой буферизируемой области, но только в некешируемой части (например, в шаблоне сайта):

$APPLICATION->ShowViewContent('myContentBlockName');
  • Если не будет выполнено ни одного AddViewContent, то в месте вывода ShowViewContent будет пусто.
  • Если не будет выполнен метод ShowViewContent, то контент переданный в AddViewContent не будет выведен нигде.
  • Если метод ShowViewContent будет выполнен в разных местах или, например, несколько раз подряд, то контент будет продублирован в каждом из этих мест.

При этом можно добавлять сколько угодно частей контента:

$APPLICATION->AddViewContent('myContentBlockName', '<p>Hello</p>');
$APPLICATION->AddViewContent('myContentBlockName', '<p>World</p>');
$APPLICATION->AddViewContent('myContentBlockName', '<p>!</p>');

Позиционировать выводимые части контента можно с помощью веса (третьего аргумента):

$APPLICATION->AddViewContent('myContentBlockName', '<span>world!</span>', 600);
$APPLICATION->AddViewContent('myContentBlockName', '<span>Hello, </span>', 500);

Как и все отложенные функции AddViewContent можно использовать только в некешируемой области, т.е. в component_epilog.php. Но в шаблоне компонента даже в кешируемой области в файлах template.php и result_modifier.php можно использовать методы SetViewTarget + EndViewTarget:

<?$this->SetViewTarget('myContentBlockName');?>
	<p>Hello</p>
<?$this->EndViewTarget();

Можно комбинировать SetViewTarget + EndViewTarget и AddViewContent. Например, в template.php передаём одну часть:

// в этом методе тоже есть возможность указать вес (второй аргумент)
<?$this->SetViewTarget('myContentBlockName', 600);?>
    <span>world</span>
<?$this->EndViewTarget();?>

А в component_epilog.php другую:

<?$APPLICATION->AddViewContent( 'myContentBlockName', '<span>Hello, </span>', 500);?>

https://blog.site-home.ru/showviewcontent.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment