Битрикс. Самописный компонент «Структура разделов»

31.12.2018

Теги: CMSWeb-разработкаБитриксИерархияКомпонентМенюНавигацияРазделИнфоблокаСтруктура

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

Файл описания компонента .description.php:

<?php
/*
 * Файл local/components/tokmakov/iblock.tree/.description.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

$arComponentDescription = array(
    'NAME' => 'Структура разделов', // название компонента
    'DESCRIPTION' => 'Выводит дерево разделов инфоблока',
    'ICON' => '/images/icon.gif', // иконка компонента относительно папки компонента
    'CACHE_PATH' => 'Y', // показывать кнопку очистки кеша
    'SORT' => 50, // порядок сортировки в визуальном редакторе
    'COMPLEX' => 'N', // признак комплексного компонента
    'PATH' => array( // расположение компонента в визуальном редакторе
        'ID' => 'other_components', // идентификатор верхнего уровеня в редакторе
        'NAME' => 'Прочие компоненты', // название верхнего уровня в редакторе
        'CHILD' => array( // второй уровень в визуальном редакторе
            'ID' => 'other_iblock', // идентификатор второго уровня в редакторе
            'NAME' => 'Информационный блок' // название второго уровня в редакторе
        )
    )
);

Настройки компонента для визуального редактора:

<?php
/*
 * Файл local/components/tokmakov/iblock.element/.parameters.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

// проверяем, установлен ли модуль «Информационные блоки»; если да — то подключаем его
if (!CModule::IncludeModule('iblock')) {
    return;
}

/*
 * Получаем массив всех типов инфоблоков — для возможности выбора
 */
$arIBlockType = CIBlockParameters::GetIBlockTypes();

/*
 * Получаем массив инфоблоков — для возможности выбора; фильтруем их по
 * выбранному типу и по активности
 */
$arInfoBlocks = array();
$arFilter = array('ACTIVE' => 'Y');
// если уже выбран тип инфоблока, выбираем инфоблоки только этого типа
if (!empty($arCurrentValues['IBLOCK_TYPE'])) {
    $arFilter['TYPE'] = $arCurrentValues['IBLOCK_TYPE'];
}
$rsIBlock = CIBlock::GetList(
    array('SORT' => 'ASC'),
    $arFilter
);
while($iblock = $rsIBlock->Fetch()) {
    $arInfoBlocks[$iblock['ID']] = '['.$iblock['ID'].'] '.$iblock['NAME'];
}

/*
 * Настройки компонента
 */
$arComponentParameters = array(
    'PARAMETERS' => array(
        // выбор типа инфоблока
        'IBLOCK_TYPE' => array(
            'PARENT' => 'BASE',
            'NAME' => 'Выберите тип инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arIBlockType,
            'REFRESH' => 'Y',
        ),
        // выбор самого инфоблока
        'IBLOCK_ID' => array(
            'PARENT' => 'BASE',
            'NAME' => 'Выберите инфоблок',
            'TYPE' => 'LIST',
            'VALUES' => $arInfoBlocks,
        ),
        // до какой глубины вложенности выбирать разделы
        'DEPTH_LEVEL' => array(
            'PARENT' => 'BASE',
            'NAME' => 'До какой глубины вложенности выбирать разделы',
            'TYPE' => 'STRING',
            'DEFAULT' => '2'
        ),
        // шаблон ссылки на страницу раздела
        'SECTION_URL' => array(
            'PARENT' => 'URL_TEMPLATES',
            'NAME' => 'URL, ведущий на страницу с содержимым раздела',
            'TYPE' => 'STRING',
            'DEFAULT' => '#SITE_DIR#/#IBLOCK_CODE#/category/id/#SECTION_ID#/'
        ),
        // настройки кеширования
        'CACHE_TIME'  =>  array('DEFAULT'=>3600),
        'CACHE_GROUPS' => array( // учитывать права доступа?
            'PARENT' => 'CACHE_SETTINGS',
            'NAME' => 'Учитывать права доступа',
            'TYPE' => 'CHECKBOX',
            'DEFAULT' => 'Y',
        ),
    ),
);

Код компонента, который выбирает разделы инфоблока с сортировкой по LEFT_MARGIN:

<?php
/*
 * Файл local/components/infoblock/iblock.tree/component.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

/** @var CBitrixComponent $this */
/** @var array $arParams */
/** @var array $arResult */
/** @var string $componentPath */
/** @var string $componentName */
/** @var string $componentTemplate */
/** @global CDatabase $DB */
/** @global CUser $USER */
/** @global CMain $APPLICATION */

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
    return;
}

if (!isset($arParams['CACHE_TIME'])) {
    $arParams['CACHE_TIME'] = 3600;
}

// тип инфоблока
$arParams['IBLOCK_TYPE'] = trim($arParams['IBLOCK_TYPE']);
// идентификатор инфоблока
$arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);
// до какой глубины выбирать разделы
$arParams['DEPTH_LEVEL'] = intval($arParams['DEPTH_LEVEL']);

// шаблон ссылки на страницу с содержимым раздела
$arParams['SECTION_URL'] = trim($arParams['SECTION_URL']);

if ($this->StartResultCache(false, $arParams['CACHE_GROUPS']==='N' ? false: $USER->GetGroups())) {

    /*
     * Выбираем все разделы инфоблока до выбранной глубины
     */

    // какие поля раздела инфоблока выбираем
    $arSelect = array(
        'ID',
        'NAME',
        'PICTURE',
        'DESCRIPTION',
        'DESCRIPTION_TYPE',
        'SECTION_PAGE_URL',
        'DEPTH_LEVEL'
    );
    // условия выборки раздела инфоблока
    $arFilter = array(
        'IBLOCK_ID' => $arParams['IBLOCK_ID'],
        'IBLOCK_ACTIVE' => 'Y',
        'ACTIVE' => 'Y',
        'GLOBAL_ACTIVE' => 'Y',
        '<=DEPTH_LEVEL' => $arParams['DEPTH_LEVEL']
    );
    // сортировка разделов для построения дерева
    $arSort = array(
        'LEFT_MARGIN' => 'ASC',
        'SORT' => 'ASC'
    );
    // выполняем запрос к базе данных
    $dbResult = CIBlockSection::GetList(
        array(),
        $arFilter,
        false,
        $arSelect
    );
    // устанавливаем шаблон пути для раздела, вместо того,
    // который указан в настройках информационного блока
    $dbResult->SetUrlTemplates('', $arParams['SECTION_URL']);
    while ($arSection = $dbResult->GetNext()) {
        // маленькая картинка раздела
        if ($arSection['PICTURE'] > 0) {
            $arSection['PICTURE'] = CFile::GetFileArray($arSection['PICTURE']);
        } else {
            $arSection['PICTURE'] = false;
        }
        $arResult[] = $arSection;
    }

    if (!empty($arResult)) { // если данные успешно получены
        $this->IncludeComponentTemplate();
    } else { // что-то пошло не так
        $this->AbortResultCache();
    }

}

Шаблон компонента просто выводит список разделов инфоблока:

<?php
/*
 * Файл local/components/tokmakov/iblock.tree/templates/.default/template.php
 */
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

/** @var array $arParams */
/** @var array $arResult */
/** @global CMain $APPLICATION */
/** @global CUser $USER */
/** @global CDatabase $DB */
/** @var CBitrixComponentTemplate $this */
/** @var string $templateName */
/** @var string $templateFile */
/** @var string $templateFolder */
/** @var string $componentPath */
/** @var CBitrixComponent $component */

$this->setFrameMode(true);
?>

<?php if (!empty($arResult)): ?>
    <ul id="iblock-section-tree">
        <?php foreach ($arResult as $arSection): ?>
            <li class="section-level-<?= $arSection['DEPTH_LEVEL']; ?>">
                <a href="<?= $arSection['SECTION_PAGE_URL']; ?>"><?= $arSection['NAME']; ?></a>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Зададим стили, чтобы получился не просто список, а дерево:

/*
 * Файл local/components/tokmakov/iblock.tree/templates/.default/style.css
 */
#iblock-section-tree > .section-level-2 {
    margin-left: 20px;
}
#iblock-section-tree > .section-level-3 {
    margin-left: 40px;
}

Массив $arResult, доступный в шаблоне компонента:

Array
(
    [0] => Array
        (
            [ID] => 16
            [NAME] => Породы кошек
            [PICTURE] => Array
                (
                    [ID] => 945
                    [MODULE_ID] => iblock
                    [HEIGHT] => 200
                    [WIDTH] => 200
                    [FILE_SIZE] => 21194
                    [CONTENT_TYPE] => image/jpeg
                    ..........
                    [SRC] => /upload/iblock/c16/c16acb4b686640c521df122f575acdb9.jpg
                )
            [DESCRIPTION] => Быть может, кошка и не лучший друг человека (это почетное звание оспаривают...
            [DESCRIPTION_TYPE] => html
            [SECTION_PAGE_URL] => /articles/category/id/16/
            [DEPTH_LEVEL] => 1
            [CODE] => porody-koshek
            [EXTERNAL_ID] => 29
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
    [1] => Array
        (
            [ID] => 17
            [NAME] => Длинношерстные
            [PICTURE] => Array
                (
                    [ID] => 984
                    [MODULE_ID] => iblock
                    [HEIGHT] => 700
                    [WIDTH] => 700
                    [FILE_SIZE] => 193845
                    [CONTENT_TYPE] => image/jpeg
                    .........
                    [SRC] => /upload/iblock/317/317346676253ea0776365b83b83c7dba.jpg
                )
            [DESCRIPTION] => Длинношерстные кошки – настоящие красавицы, которые не оставят равнодушными...
            [DESCRIPTION_TYPE] => text
            [SECTION_PAGE_URL] => /articles/category/id/17/
            [DEPTH_LEVEL] => 2
            [CODE] => dlinnosherstnye
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
    [2] => Array
        (
            [ID] => 18
            [NAME] => Короткошерстные
            [PICTURE] => Array
                (
                    [ID] => 983
                    [MODULE_ID] => iblock
                    [HEIGHT] => 700
                    [WIDTH] => 700
                    [FILE_SIZE] => 71596
                    [CONTENT_TYPE] => image/jpeg
                    ..........
                    [SRC] => /upload/iblock/17b/17ba9590bb537f17a9eb8a5edb912819.jpg
                )
            [DESCRIPTION] => Короткошёрстные кошки очень полюбились владельцам квартир, так как они не требуют...
            [SECTION_PAGE_URL] => /articles/category/id/18/
            [DEPTH_LEVEL] => 2
            [CODE] => korotkosherstnye
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
    [3] => Array
        (
            [ID] => 19
            [NAME] => Породы собак
            [PICTURE] => Array
                (
                    [ID] => 951
                    [MODULE_ID] => iblock
                    [HEIGHT] => 200
                    [WIDTH] => 200
                    [FILE_SIZE] => 20142
                    [CONTENT_TYPE] => image/jpeg
                    ..........
                    [SRC] => /upload/iblock/54b/54b6016bcfdda68acb57b3f7e54a1b73.jpg
                )
            [DESCRIPTION] => Более 20 тысяч лет назад первобытный человек привел в свое жилище совершенно...
            [DESCRIPTION_TYPE] => html
            [SECTION_PAGE_URL] => /articles/category/id/19/
            [DEPTH_LEVEL] => 1
            [CODE] => porody-sobak
            [EXTERNAL_ID] => 28
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
    [4] => Array
        (
            [ID] => 20
            [NAME] => Декоративные породы
            [PICTURE] => Array
                (
                    [ID] => 953
                    [MODULE_ID] => iblock
                    [HEIGHT] => 200
                    [WIDTH] => 200
                    [FILE_SIZE] => 25689
                    [CONTENT_TYPE] => image/jpeg
                    ..........
                    [SRC] => /upload/iblock/ffa/ffa1f34cc799b4bcc86381ecd06e332f.jpg
                )
            [DESCRIPTION] => Декоративные собаки — собирательное определение пород собак, не предназначенных...
            [DESCRIPTION_TYPE] => html
            [SECTION_PAGE_URL] => /articles/category/id/20/
            [DEPTH_LEVEL] => 2
            [CODE] => dekorativnye-porody
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
    [5] => Array
        (
            [ID] => 21
            [NAME] => Служебные породы
            [PICTURE] => Array
                (
                    [ID] => 955
                    [MODULE_ID] => iblock
                    [HEIGHT] => 200
                    [WIDTH] => 200
                    [FILE_SIZE] => 24596
                    [CONTENT_TYPE] => image/jpeg
                    ..........
                    [SRC] => /upload/iblock/b1a/b1a0c6fed22467a3da087c3fdae64241.jpg
                )
            [DESCRIPTION] => К ним относится ряд пород, которые человек использует для работы. Использование...
            [DESCRIPTION_TYPE] => html
            [SECTION_PAGE_URL] => /articles/category/id/21/
            [DEPTH_LEVEL] => 2
            [CODE] => sluzhebnye-porody
            [IBLOCK_TYPE_ID] => content
            [IBLOCK_ID] => 5
            [IBLOCK_CODE] => articles
            [GLOBAL_ACTIVE] => Y
        )
)

Чтобы сформировать многоуровневый список, нужно модифицировать массив $arResult

<?php
/*
 * Файл local/components/tokmakov/iblock.tree/templates/.default/result_modifier.php
 */
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

if (!empty($arResult)) {
    foreach ($arResult as $key => $value) {
        $arResult[$key]['IS_PARENT'] = false;
        if (isset($arResult[$key+1]) && $arResult[$key+1]['DEPTH_LEVEL'] > $value['DEPTH_LEVEL']) {
            $arResult[$key]['IS_PARENT'] = true;
        }
    }
}

Тогда шаблон компонента будет таким:

<?php
/*
 * Файл local/components/tokmakov/iblock.tree/templates/.default/template.php
 */
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

/** @var array $arParams */
/** @var array $arResult */
/** @global CMain $APPLICATION */
/** @global CUser $USER */
/** @global CDatabase $DB */
/** @var CBitrixComponentTemplate $this */
/** @var string $templateName */
/** @var string $templateFile */
/** @var string $templateFolder */
/** @var string $componentPath */
/** @var CBitrixComponent $component */

$this->setFrameMode(true);
?>

<?php if (!empty($arResult)): ?>
    <ul>
    <?php $previousLevel = 0; ?>
    <?php foreach ($arResult as $arSection): ?>

        <?php if ($previousLevel && $arSection['DEPTH_LEVEL'] < $previousLevel): ?>
            <?= str_repeat('</ul></li>', ($previousLevel - $arSection['DEPTH_LEVEL'])); ?>
        <?php endif; ?>

        <?php if ($arSection['IS_PARENT']): /* если есть дочерние элементы */ ?>
            <?php if ($arSection['DEPTH_LEVEL'] == 1): /* корневой элемент */ ?>
                <li class="root parent">
                    <a href="<?= $arSection['SECTION_PAGE_URL']; ?>"><?= $arSection['NAME']; ?></a>
                    <ul>
            <?php else: /* элемент второго, третьего уровня */ ?>
                <li class="parent">
                    <a href="<?= $arSection['SECTION_PAGE_URL']; ?>"><?= $arSection['NAME']; ?></a>
                    <ul>
            <?php endif; ?>
        <?php else: /* если нет дочерних элементов */ ?>
            <?php if ($arSection['DEPTH_LEVEL'] == 1): /* корневой элемент */ ?>
                <li class="root">
                    <a href="<?= $arSection['SECTION_PAGE_URL']; ?>"><?= $arSection['NAME']; ?></a>
                </li>
            <?php else: /* элемент второго, третьего уровня */ ?>
                <li>
                    <a href="<?= $arSection['SECTION_PAGE_URL']; ?>"><?= $arSection['NAME']; ?></a>
                </li>
            <?php endif; ?>
        <?php endif; ?>

        <?php $previousLevel = $arSection['DEPTH_LEVEL']; ?>

    <?php endforeach; ?>

    <?php if ($previousLevel > 1): ?>
        <?= str_repeat('</ul></li>', ($previousLevel-1)); ?>
    <?php endif; ?>
    </ul>
<?php endif; ?>

А сформированный шаблоном html-код будет таким:

<ul>
    <li class="root parent">
        <a href="/articles/category/id/16/">Породы кошек</a>
        <ul>
            <li>
                <a href="/articles/category/id/17/">Длинношерстные</a>
            </li>
            <li>
                <a href="/articles/category/id/18/">Короткошерстные</a>
            </li>
        </ul>
    </li>        
    <li class="root parent">
        <a href="/articles/category/id/19/">Породы собак</a>
        <ul>
            <li>
                <a href="/articles/category/id/20/">Декоративные породы</a>
            </li>
            <li>
                <a href="/articles/category/id/21/">Служебные породы</a>
            </li>
        </ul>
    </li>
</ul>

Пример вызова компонента:

$APPLICATION->IncludeComponent(
    "tokmakov:iblock.tree",
    "",
    Array(
        "CACHE_GROUPS" => "Y",
        "CACHE_TIME" => "3600",
        "CACHE_TYPE" => "A",
        "DEPTH_LEVEL" => "2",
        "IBLOCK_ID" => "5",
        "IBLOCK_TYPE" => "content",
        "SECTION_URL" => "#SITE_DIR#/#IBLOCK_CODE#/category/id/#SECTION_ID#/"
    )
);

Поиск: CMS • Web-разработка • Битрикс • Иерархия • Компонент • Меню • Навигация • Раздел инфоблока

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.