Принцип поиска соседних элементов

Подобная задача часть возникает для того, чтобы на детальной странице вывести ссылки на соседние статьи или товары.

Но алгоритм их поиска не совсем очевидный, особенно учитывая, что компоненту на странице раздела, например, bitrix:news.list еще задана определенная сортировка вывода элементов. Так что просто искать по ID ± 1 не выйдет. К тому же соседние по ID элементы могут оказаться из другого инфоблока, либо эти элементы вообще могли удаляться в админке, а значит такого ID просто не будет.

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

Код получения соседних элементов

Привожу код, который позволяет получить соседние элементы в инфоблоке:

<?php

use Bitrix\Main\Loader;

function get_prev_next_elements($iblockId, $currentId, $sort = ['SORT' => 'ASC'], $filter = [])
{
    //подключаем модуль ИБ
    Loader::includeModule('iblock');

    $filter['IBLOCK_ID'] = $iblockId;
    $resEls = CIBlockElement::GetList($sort, $filter);

    $allElements = [];

    //получаем все элементы
    while ($el = $resEls->Fetch()) {
        $allElements[$el['ID']] = $el;
    }

    if (count($allElements) === 1 || !key_exists($currentId, $allElements)) {
        //если элемент один или текущего нет в списке, возвращаем false
        return false;
    }

    //перебираем элементы, чтобы найти текущий
    while (key($allElements) != $currentId) next($allElements);

    $prev = prev($allElements);

    if (!$prev) {
        //если предыдущего нет, то им становится последний
        $prev = $allElements[array_key_last($allElements)];
        reset($allElements);
        $next = next($allElements);
    } else {
        next($allElements);
        $next = next($allElements);

        if (!$next) {
            //если следующего нет, то им становится первый
            $next = $allElements[array_key_first($allElements)];
        }
    }

    return [
        'PREV' => $prev,
        'NEXT' => $next,
    ];
}

Функция принимает на вход ID инфоблока и ID текущего элемента. Также дополнительно можно передать сортировку и фильтрацию элементов по правилам CIBlockElement::GetList().

Пример использования для страницы новости

Пример использования данной функции вы можете увидеть как раз на этой странице в конце статьи.

Реализуется подобный вариант следующим образом.

В файле …/news.detail/<шаблон>/result_modifier.php шаблона детальной страницы статьи добавлено получение этих самых соседних элементов (а точнее в данном случае только ссылок):

<?php

$prevNextEls = get_prev_next_elements($arResult['ID'], $arResult['IBLOCK_ID'], [
    'ID' => 'DESC', //сортировка как у news.list
    'SORT' => 'ASC',
]);

$arResult['PREV_NEXT_BUTTONS'] = $prevNextEls ? [
    'PREV' => $prevNextEls['PREV']['DETAIL_PAGE_URL'],
    'NEXT' => $prevNextEls['NEXT']['DETAIL_PAGE_URL'],
] : false;

А в файле шаблона template.php уже непосредственно сам вывод:

<?php if ($prevNext = $arResult['PREV_NEXT_BUTTONS']): ?>
    <a href="<?= $prevNext['PREV'] ?>">
        Предыдущая статья
    </a>
    <a href="<?= $prevNext['NEXT'] ?>">
        Следующая статья
    </a>
<?php endif; ?>

Также хочу обратить внимание на свой модуль, который использует как раз данную функцию, и добавляет кнопки перехода к предыдущему и следующему элементам инфоблока в админке на странице редактирования элемента: «Кнопки быстрого перехода к предыдущему и следующему элементу инфоблока».