Полный цикл в digital

Меню из разделов инфоблока с элементами инфоблока

Готовый код можно скачать в моем репозитории на GitFlic.

В битриксе можно создавать меню из разделов инфоблока, для этого к компоненту menu подключается файл типа .*.menu_ext.php, подробнее об этом тут, в нем вызывается компонент menu.sections. У этого подхода нет возможности включать в меню элементы разделов, а это иногда бывает нужно.

Для этого я сделал компонент menu.sections.elements, подключается он так же в файле .*.menu_ext.php.

.description.php

menu.sections.elements/.description.php<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
$arComponentDescription = array(
    // название компонента
    'NAME' => 'Список разделов и элементов для sitemap.html',
    // описание компонента
    'DESCRIPTION' => 'Получаем список разделов и элементов для компонента main.map',
    // путь к иконке компонента относительно папки компонента
    'ICON' => '/images/eaddlist.gif',
    // показывать кнопку очистки кеша
    'CACHE_PATH' => 'Y',
    // порядок сортировки в визуальном редакторе
    'SORT' => 30,
    // признак комплексного компонента
    'COMPLEX' => 'N',
    // расположение компонента в визуальном редакторе
    'PATH' => array(
        // идентификатор верхнего уровеня в редакторе                                        
        'ID' => 'likee',
        // название верхнего уровня в редакторе                                 
        'NAME' => 'Лайки',
    )
);

.parameters.php

menu.sections.elements/.parameters.php<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// проверяем установку модуля «Информационные блоки»
if (!CModule::IncludeModule('iblock')) {
    return;
}
// получаем массив всех типов инфоблоков для возможности выбора
$arTypesEx = CIBlockParameters::GetIBlockTypes(array("all" => " "));
// пустой массив для вывода
$arIBlocks = array();
// метод выборки информационных блоков
$db_iblock = CIBlock::GetList(array("SORT" => "ASC"), array("SITE_ID" => $_REQUEST["site"], "TYPE" => ($arCurrentValues["IBLOCK_TYPE"] != "all" ? $arCurrentValues["IBLOCK_TYPE"] : "")));
// перебираем и выводим в адмику доступные информационные блоки
while ($arRes = $db_iblock->Fetch()) {
    $arIBlocks[$arRes["ID"]] = $arRes["NAME"];
}
$arComponentParameters = array(
    "GROUPS" => array(),
    "PARAMETERS" => array(
        "IBLOCK_TYPE" => array(
            "PARENT" => "BASE",
            "NAME" => "Выберите тип инфоблока",
            "TYPE" => "LIST",
            "VALUES" => $arTypesEx,
            "DEFAULT" => "",
            "ADDITIONAL_VALUES" => "N",
            "REFRESH" => "Y",
        ),
        "IBLOCK_ID" => array(
            "PARENT" => "BASE",
            "NAME" => "Выберите родительский инфоблок",
            "TYPE" => "LIST",
            "VALUES" => $arIBlocks,
            "DEFAULT" => '',
            "MULTIPLE" => "N",
            "ADDITIONAL_VALUES" => "N",
            "REFRESH" => "Y",
        ),
        "DEPTH_LEVEL" => array(
            "PARENT" => "DATA_SOURCE",
            "NAME" => "Уровень вложенности",
            "TYPE" => "STRING",
            "DEFAULT" => "1",
        ),
        "DELETE_SECTION" => array(
            "PARENT" => "DATA_SOURCE",
            "NAME" => "Исключения по ID для секций",
            "TYPE" => "STRING",
            "DEFAULT" => "",
        ),
        "DELETE_ELEMENT" => array(
            "PARENT" => "DATA_SOURCE",
            "NAME" => "Исключения по ID для секций",
            "TYPE" => "STRING",
            "DEFAULT" => "",
        ),
        "CACHE_TIME" => array("DEFAULT" => 36000000),
    ),
);

class.php

menu.sections.elements/class.php<?php
/**
 * @author Эйч Маркетинг <info@hmarketing.ru>
 * @copyright 2014-2024 The hmarketing.ru
 */
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// пространство имен для работы с языковыми файлами
use Bitrix\Main\Localization\Loc;
// пространство имен для всех исключений в системе
use Bitrix\Main\SystemException;
// пространство имен для загрузки необходимых файлов, классов, модулей
use Bitrix\Main\Loader;
// пространство имен для работы с кешем
use \Bitrix\Main\Data\Cache;
use \Bitrix\Main\Application;
/**
 * основной класс компонента
 * 
 */
class sitemapHtml extends CBitrixComponent
{
    /**
     * ID секций для фильтра
     *
     * @var array
     */
    public $arSectionId = array();
    /**
     * данные элементов и разделов
     *
     * @var array
     */
    public $menuItems = array();
    /**
     * выполняет основной код компонента, аналог конструктора (метод подключается автоматически)
     *
     * @return void
     */
    public function executeComponent()
    {
        try {
            // подключаем метод проверки подключения модуля «Информационные блоки»
            $this->checkModules();
            // подключаем метод подготовки массива $arResult
            return $this->getResult();
        } catch (SystemException $e) {
            ShowError($e->getMessage());
        }
    }
    /**
     * подключение языковых файлов (метод подключается автоматически) 
     *
     * @return void
     */
    public function onIncludeComponentLang()
    {
        Loc::loadMessages(__FILE__);
    }
    /**
     * проверяем установку модуля «Информационные блоки» (метод подключается внутри класса try...catch) 
     *
     * @return void
     */
    protected function checkModules()
    {
        // если модуль не подключен
        if (!Loader::includeModule('iblock')) {
            // выводим сообщение в catch
            throw new SystemException(Loc::getMessage('IBLOCK_MODULE_NOT_INSTALLED'));
        }
    }
    /**
     * обработка массива $arParams (метод подключается автоматически)  
     *
     * @param  array $arParams
     * @return array
     */
    public function onPrepareComponentParams($arParams)
    {
        // настройки $arParams кеширование
        if (!isset($arParams['CACHE_TIME'])) {
            $arParams['CACHE_TIME'] = 3600;
        } else {
            $arParams['CACHE_TIME'] = intval($arParams['CACHE_TIME']);
        }
        // настройки $arParams ID инфоблока
        $arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);
        // настройки $arParams уровень 
        $arParams['DEPTH_LEVEL'] = intval($arParams['DEPTH_LEVEL']);
        // настройки $arParams исключение секций
        $arParams['DELETE_SECTION'] = preg_replace("#[^0-9\,]#", '', $arParams['DELETE_SECTION']);
        // настройки $arParams исключение элементов
        $arParams['DELETE_ELEMENT'] = preg_replace("#[^0-9\,]#", '', $arParams['DELETE_ELEMENT']);
        return $arParams;
    }
    /**
     * подготовка массива $arResult (метод подключается внутри класса try...catch)
     *
     * @return array
     */
    protected function getResult()
    {
        // служба кеширования
        $cache = Cache::createInstance();
        // служба пометки кеша тегами
        $taggedCache = Application::getInstance()->getTaggedCache();
        $cachePath = 'sitemap-html';
        $cacheTtl = $this->arParams['CACHE_TIME'];
        $cacheKey = 'sitemapHtm';
        // если кеш есть
        if ($cache->initCache($cacheTtl, $cacheKey, $cachePath)) {
            $this->arResult = $cache->getVars();
            return $this->arResult;
        }
        // если кеша нет
        elseif ($cache->startDataCache()) {
            // начинаем записывать тегированый кеш
            $taggedCache->startTagCache($cachePath);
            // кеш сбрасываем при изменении данных в инфоблоке с ID который передаем
            $taggedCache->registerTag('iblock_id_' . $this->arParams['IBLOCK_ID']);
            //*********************************//
            // Секции                          //
            //*********************************//
            // поля 
            $arSelect = array(
                'ID',
                'DEPTH_LEVEL',
                'NAME',
                'SECTION_PAGE_URL',
                'IBLOCK_SECTION_ID',
            );
            // фильтр
            $arFilter = array(
                'IBLOCK_ID' => $this->arParams['IBLOCK_ID'],
                'GLOBAL_ACTIVE' => 'Y',
                'ACTIVE' => 'Y',
                '<=DEPTH_LEVEL' => $this->arParams['DEPTH_LEVEL'],
                '!=ID' => [$this->arParams['DELETE_SECTION']],
            );
            // сортировка
            $arOrder = array(
                'SORT' => 'ASC',
            );
            // запрос
            $rsSections = CIBlockSection::GetList($arOrder, $arFilter, false, $arSelect);
            // перебираем секции
            while ($arSection = $rsSections->GetNext()) {
                // секция всегда 1
                $arSection['IS_SECTION'] = 1;
                // ссылка на раздел
                $arSection['LINK'] = $arSection['SECTION_PAGE_URL'];
                // проверяем ID группы, если не задан то корень
                if ($arSection['IBLOCK_SECTION_ID']) {
                    $this->menuItems[$arSection['IBLOCK_SECTION_ID']][] = $arSection;
                } else {
                    $this->menuItems['ROOT'][] = $arSection;
                }
                // записываем ID секции
                $arSectionId[] = $arSection['ID'];
            }
            //*********************************//
            // Элементы                        //
            //*********************************//
            // поля
            $arSelect = array(
                'ID',
                'NAME',
                'DETAIL_PAGE_URL',
                'IBLOCK_SECTION_ID'
            );
            // фильтр
            $arFilter = array(
                'IBLOCK_ID' => $this->arParams['IBLOCK_ID'],
                'ACTIVE' => 'Y',
                array(
                    'LOGIC' => 'OR',
                    array('SECTION_ID' => $arSectionId),
                    array('SECTION_ID' => false),
                ),
                '!=ID' => [$this->arParams['DELETE_ELEMENT']],
            );
            // сортировка
            $arOrder = array(
                'SORT' => 'ASC'
            );
            // запрос
            $res = CIBlockElement::GetList($arOrder, $arFilter, false, false, $arSelect);
            // перебираем элементы
            while ($ob = $res->GetNextElement()) {
                // получаем элемент
                $arFields = $ob->GetFields();
                // элемент всегда 0
                $arFields['IS_SECTION'] = 0;
                // ссылка на элемент
                $arFields['LINK'] = $arFields['DETAIL_PAGE_URL'];
                // проверяем ID группы, если не задан то корень
                if ($arFields['IBLOCK_SECTION_ID']) {
                    $this->menuItems[$arFields['IBLOCK_SECTION_ID']][] = $arFields;
                } else {
                    $this->menuItems['ROOT'][] = $arFields;
                }
            }
            // прогоняем через метод
            $this->createMenuArray($this->menuItems['ROOT'], 1);
            // заканчиваем записывать тегированый кеш
            $taggedCache->endTagCache();
            // записываем результат в тегированный кеш
            $cache->endDataCache($this->arResult);
            return $this->arResult;
        }
    }
    /**
     * наполняем массив $arResult разделяя корневые элементы и вложенные
     *
     * @param  array $menuItemsRoot корневые элементы
     * @param  int $depthLevel уровень вложенности
     * @return void
     */
    function createMenuArray($menuItemsRoot, $depthLevel)
    {
        // перебираем корневые элементы
        foreach ($menuItemsRoot as $item) {
            // если секция и в $menuItems есть вложенные элементы
            $isParent = ($item['IS_SECTION'] && isset($this->menuItems[$item['ID']]));
            // наполняем $arResult
            $this->arResult[] = array(
                htmlspecialchars($item['~NAME']),
                $item['LINK'],
                array(),
                array(
                    'FROM_IBLOCK' => true,
                    'IS_PARENT' => $isParent,
                    'DEPTH_LEVEL' => $depthLevel,
                ),
            );
            // если секция и в $menuItems есть вложенные элементы, дергаем createMenuArray и заполняем ими $arResult
            if ($isParent) {
                $this->createMenuArray($this->menuItems[$item['ID']], $depthLevel + 1);
            }
        }
    }
}

Вызов в .*.menu_ext.php

.sitemap_section.menu_ext.php<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
global $APPLICATION;
$aMenuLinksExt = $APPLICATION->IncludeComponent(
    "likee:menu.sections.elements",
    "",
    array(
        "IBLOCK_TYPE" => "POLEZNOE",
        "IBLOCK_ID" => "14",
        "DEPTH_LEVEL" => "5",
        "CACHE_TYPE" => "A",
        "DELETE_SECTION" => "",
        "DELETE_ELEMENT" => "",
        "CACHE_TIME" => "3600"
    ),
    false
);
$aMenuLinks = array_merge($aMenuLinksExt, $aMenuLinks);
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг