Очередной пост в копилку ORM D7 в Битриксе. На этот раз будем получать соседей нужного элемента инфоблока. Под катом много кода.

Того же самого можно добиться методом CIBlockElement::GetList() с параметрами nElementID и nPageSize для постраничной навигации. SQL-запросы для определения соседей я честно от туда и взял.

//<?php
 
\Bitrix\Main\Loader::includeModule('iblock');
 
// Сюда мы и запишем массивы соседних элементов
$nextElement = null;
$prevElement = null;
 
// Id элемента, соседей которого надо выбрать
$elementId = 104;
 
$element = \Bitrix\Iblock\ElementTable::getRowById($elementId);
 
$sectionId = $element['IBLOCK_SECTION_ID'];
 
// Количество соседей (в примере моего кода поддерживается 1)
$nPageSize = 1;
 
// Устанавливаем нужный фильтр
$filter = array(
    '=IBLOCK_ID' => $element['IBLOCK_ID'],
    '=ACTIVE' => 'Y',
);
 
// Составляем запрос для фильтрации по секции элемента (поддерживает множественную привязку к разделам)
$sectionElementSubQuery = new Bitrix\Main\Entity\Query('\Bitrix\Iblock\SectionElementTable');
 
$sectionElementSubQuery
    ->addSelect('IBLOCK_ELEMENT_ID')
    ->setFilter(array('=IBLOCK_SECTION_ID' => $sectionId))
    ->registerRuntimeField('SECTION', array(
        'data_type' => '\Bitrix\Iblock\SectionTable',
        'reference' => array(
            '=this.IBLOCK_SECTION_ID' => 'ref.ID',
        ),
        'join_type' => 'inner'
    ));
 
// Сохраняем полученный sql-запрос, не выполняя его
$sectionElementSubQuerySql = $sectionElementSubQuery->getQuery();
 
$sectionElementEntity = Bitrix\Main\Entity\Base::compileEntity('OlegproSectionElementEntity',
    array('IBLOCK_ELEMENT_ID' => array('data_type' => 'integer')),
    array('table_name' => sprintf('(%s)', $sectionElementSubQuerySql))
);
 
// Составляем sql-запрос для получение элементов с нужными фильтрами
$querySiblingsElement = new Bitrix\Main\Entity\Query(Bitrix\Iblock\ElementTable::getEntity());
 
$querySiblingsElement
    ->setSelect(array(
        'ID',
        'NAME',
        'CODE',
        'DETAIL_PICTURE',
        'IBLOCK_SECTION_ID',
    ))
    ->registerRuntimeField('SECTION_ELEMENT', array(
        'data_type' => $sectionElementEntity->getDataClass(),
        'reference' => array(
            '=this.ID' => 'ref.IBLOCK_ELEMENT_ID'
        ),
        'join_type' => 'inner'
    ))
    ->setFilter($filter)
    ->addOrder('ID', 'DESC');
 
// Сохраняем полученный sql-запрос, не выполняя его
$sqlQuerySiblingsElement = $querySiblingsElement->getQuery();
 
 
$connection = Bitrix\Main\Application::getConnection();
 
$connection->query('SET @rank=0');
 
$connection->query(
    sprintf("
            SELECT @rank:=el1.rank
            FROM (
                SELECT @rank:=@rank+1 AS rank, el0.*
                FROM (
                    %s
                ) el0
            ) el1
            WHERE el1.ID = %d
        ", $sqlQuerySiblingsElement, $elementId)
);
 
$connection->query('SET @rank2=0');
 
$siblingsElementIterator = $connection->query(
    sprintf("
            SELECT *
            FROM (
                SELECT @rank2:=@rank2+1 AS RANK, el0.*
                FROM (
                    %s
                ) el0
            ) el1
            WHERE el1.RANK between @rank-%d and @rank+%d
        ", $sqlQuerySiblingsElement, $nPageSize, $nPageSize)
);
 
$siblingsElements = array();
 
while ($siblingsElement = $siblingsElementIterator->fetch()) {
    $siblingsElements[] = $siblingsElement;
}
 
if (sizeof($siblingsElements) == 3) {
    list($prevElement, , $nextElement) = $siblingsElements;
} elseif (sizeof($siblingsElements) == 2) {
    if ($siblingsElements[0]['ID'] != $elementId) {
        $prevElement = $siblingsElements[0];
    } else {
        $nextElement = $siblingsElements[1];
    }
}
 
echo '<pre>';print_r($prevElement);echo '</pre>'; 
echo '<pre>';print_r($nextElement);echo '</pre>';

Ну вот и всё. В переменных $prevElement и $nextElement будут массивы (или NULL, если таковых не найдётся) сдедующего и предыдущего соседа элемента с id = 104.

Это лишь простой пример составления запроса для получения соседей. В реале: у меня через runtime поля подключается кастомная таблица, в которой хранится индекс сортировки элемента, для конкретной категории (нужно для возможности независимой сортировки элемента в любой из категорий при множественной привязке к категориям). Как вы понимаете, CIBlockElement::GetList() тут не обойтись.