D7 для работы с элементами инфоблока

В 20 версии битрикса, на смену старому api, наконец то, пришел новый "зверь" - D7. Его задачей было заменить старое api и сделать битрикс современным фраемворком, с чем он в общем то и не особо то и справился. Чтобы поддерживать старые версии битрикса было решено поддерживать обратную совместимость со старым api. Как и прежде, стоит в первую очередь подключить модуль iblock:

\Bitrix\Main\Loader::includeModule('iblock');

Как и в старом ядре, мы можем использовать сортировку, фильтрацию и прочие прелести D7. Одной из особенностей нового ядра, является удобная работа с кешированием, которая сводится к добавлению одного параметра в запрос. Для получения элементов, стоит использовать \Bitrix\Iblock\ElementTable::getList:

$elements = \Bitrix\Iblock\ElementTable::getList([
	'order' => ['SORT' => 'ASC'], // сортировка
	'select' => ['ID', 'NAME', 'IBLOCK_ID', 'SORT', 'TAGS'], // выбираемые поля
	'filter' => ['IBLOCK_ID' => 1], // фильтр только по полям элемента, свойства (PROPERTY) использовать нельзя
	'group' => ['TAGS'], // группировка по полю, order должен быть пустой
	'limit' => 1000, // целое число, ограничение выбираемого кол-ва
	'offset' => 0, // целое число, указывающее номер первого столбца в результате
	'count_total' => 1, // дает возможность получить кол-во элементов через метод getCount()
	'runtime' => [], // массив полей сущности, создающихся динамически
	'data_doubling' => false, // разрешает получение нескольких одинаковых записей
	'cache' => [ // Кеш запроса, почему-то в офф. документации об этом умалчивают
		'ttl' => 3600,
		'cache_joins' => true
	],
]);

Для получения записей можно использовать следующие методы:

$elements->fetch(); // или $dbItems->fetchRaw() получение одной записи, можно перебрать в цикле while ($arItem = $dbItems->fetch())
$elements->fetchAll(); // получение всех записей
$elements->getCount(); // кол-во найденных записей без учета limit, доступно если при запросе было указано count_total = 1
$elements->getSelectedRowsCount(); // кол-во полученных записей с учетом limit

Если мы будем использовать $elements->fetch(), то будет выбрана первая запись, либо делаем как по старинке, перебираем в while. Поэтому следует выбирать $elements->fetchAll():

$elements = \Bitrix\Iblock\ElementTable::getList([
	'select' => ['ID', 'NAME', 'IBLOCK_ID'],
	'filter' => ['IBLOCK_ID' => $arParams['IBLOCK_ID']]
])->fetchAll();

foreach ($elements as $element) {
    //...
}

/* Array
(
    [ID] => 7
    [NAME] => Начало
    [IBLOCK_ID] => 2
)
*/

Выбрать этими методами свойства элементами не получится, поэтому в данном случае стоит использовать совместную конструкцию нового api и старого:

$elements = \Bitrix\Iblock\ElementTable::getList([
	'select' => ['ID', 'NAME', 'IBLOCK_ID'],
	'filter' => ['IBLOCK_ID' => $arParams['IBLOCK_ID']]
])->fetchAll();

foreach ($elements as $element) {
    $dbProperty = \CIBlockElement::getProperty(
        $element['IBLOCK_ID'],
        $element['ID']
    );
    while($arProperty = $dbProperty->Fetch()){  
        $arItem['PROPERTIES'][] = $arProperty;
    }
}

Чтобы получить данные конкретного элемента, стоит использовать \Bitrix\Iblock\ElementTable::getByPrimary - это аналог CIBlockElement::GetByID с возможностью выборки определенных значений:

$element = \Bitrix\Iblock\ElementTable::getByPrimary($elementID, [
    'select' => ['ID', 'NAME', 'IBLOCK_ID']
])->fetch();

/* Array
(
    [ID] => 7
    [NAME] => Начало
    [IBLOCK_ID] => 2
)
*/

Так же, для работы с инфоблоками в новом ядре можно гораздо удобнее. Для этого надо задать в настройках инфоблока "Символьный код API", именно его мы будем использовать для идентификации инфоблоков, а не их ID. Тут будет работать так называемый стиль "CamelCase" - стиль написания составных слов, при котором несколько слов пишутся слитно без пробелов, при этом каждое слово внутри фразы пишется с прописной буквы. Если вы задали символьный код API такой: "catalog_new", то для доступа к элементами инфоболока стоит обращаться к \Bitrix\Iblock\Elements\ElementCatalogNewTable. Т.е. мы пользуемся классом \Bitrix\Iblock\Elements\ElementXXXXXTable, где XXXXX - Символьный код API

А дальше есть два варианта. Первый - мы работаем как обычно, т.е. с массивами или вариант два - это работаем с объектами. Рассмотрим первый вариант - работа с массивом.

$elements = \Bitrix\Iblock\Elements\ElementCatalogNewTable::getList([
    'select' => ['ID', 'NAME', 'CML2_ARTICLE'],
    'filter' => ['=ACTIVE' => 'Y'],
])->fetchAll();
foreach ($elements as $element) {
    // ...
}
/*Array
(
    [ID] => 7
    [NAME] => Начало
    [IBLOCK_ELEMENTS_ELEMENT_FEEDBACK_CML2_ARTICLE_ID] => 4
    [IBLOCK_ELEMENTS_ELEMENT_FEEDBACK_CML2_ARTICLE_IBLOCK_ELEMENT_ID] => 7
    [IBLOCK_ELEMENTS_ELEMENT_FEEDBACK_CML2_ARTICLE_IBLOCK_PROPERTY_ID] => 3
    [IBLOCK_ELEMENTS_ELEMENT_FEEDBACK_CML2_ARTICLE_VALUE] => AL5665LA
)*/

Здесь стоит отдельно заострить внимание на параметре select. Чтобы было удобно работать с результатами выборки свойств инфоблока, стоит прописывать их так: 'CML2_ARTICLE_' => 'CML2_ARTICLE':

$elements = \Bitrix\Iblock\Elements\ElementCatalogNewTable::getList([
    'select' => ['ID', 'NAME', 'CML2_ARTICLE_' => 'CML2_ARTICLE'],
    'filter' => ['=ACTIVE' => 'Y'],
])->fetchAll();
foreach ($elements as $element) {
    // ...
}
/*Array
(
    [ID] => 7
    [NAME] => Начало
    [EMAIL_IBLOCK_ELEMENT_ID] => 7
    [EMAIL_IBLOCK_PROPERTY_ID] => 3
    [EMAIL_VALUE] => AL5665LA
)*/

По аналогии с \Bitrix\Iblock\ElementTable здесь так же можно обращаться к элементу через getByPrimary:

$element = \Bitrix\Iblock\Elements\ElementCatalogNewTable::getByPrimary($elementID, [
    'select' => ['ID', 'NAME', 'CML2_ARTICLE_' => 'CML2_ARTICLE']
])->fetch();
/*Array
(
    [ID] => 7
    [NAME] => Начало
    [EMAIL_IBLOCK_ELEMENT_ID] => 7
    [EMAIL_IBLOCK_PROPERTY_ID] => 3
    [EMAIL_VALUE] => AL5665LA
)*/

Теперь стоит подробнее рассмотреть, как работать с элемнтами через объекты. Тут мы используем fetchObject, чтобы получить один объект и fetchCollection для получения коллекции объектов

Здесь можем обращаться через функционал основанный на CamelCase или через \Bitrix\Iblock\ElementTable. При работае с объектом, для получения данных поля, используем так же стиль CamelCase, т.е. обращаемся getXxx. Название элемента получаем через getName. Для примера рассмотрим разные варианты.

Получим элемент и будем работать с ним, как с объектом:

$element = \Bitrix\Iblock\ElementTable::getByPrimary($elementID, [
    'select' => ['ID', 'NAME', 'IBLOCK_ID']
])->fetchObject();

$name = $element->getName();//Начало
$id = $element->getId();//7
$iblock_id = $element->getIblockId();//1

Так же получить любое значение описаное в 'select' можно через get:

$element = \Bitrix\Iblock\Elements\ElementCatalogNewTable::getList([
    'select' => ['ID', 'NAME', 'IBLOCK_ID'],
    'filter' => [
        'ID' => $elementID,
    ],
])->fetchObject();

$name = $element->get('NAME');//Начало
$id = $element->get('ID');//7
$iblock_id = $element->get('IBLOCK_ID');//1

В ORM нет возможности просто получить DETAIL_PAGE_URL, для этого необходимо добавить в выборку 'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL', но и это еще не все. Таким образом мы получим шаблон для построения УРЛ:

#SITE_DIR#/catalog/#SECTION_CODE_PATH#/#ELEMENT_CODE#/

Для того, чтобы это преобразовать в нужный УРЛ, необходимо пользоваться CIBlock::ReplaceDetailUrl. К сожалению, с документацией на этот функционал туговато, но мы можем заглянуть "под капот" ибо расположен функционал тут: /bitrix/modules/iblock/classes/general/iblock.php:2800. Вызывается метод просто: CIBlock::ReplaceDetailUrl($url, $arr, $server_name = false, $arrType = false). Тут есть несколько важных моментов: $url - это шаблон построения ссылки, то что мы полчили выше в DETAIL_PAGE_URL. $arr - тут должны передаваться все необходимые для постороения параметры. Если у Вас указан #SECTION_CODE_PATH# - то в массив надо передать IBLOCK_SECTION_ID, если есть #ELEMENT_CODE# - то надо передать CODE элемента. И важный параметр под номером 4 - здесь надо указать 'E', тогда функционал сам сможент построить SECTION_CODE_PATH, иначе - он этого не сделает. В итоге, чтобы получить элемент и ссылку на него надо:

$dbItems = \Bitrix\Iblock\ElementTable::getList(array(
    'select' => array(
        'ID',
        'IBLOCK_SECTION_ID',
        'CODE',
        'NAME',
        'IBLOCK_ID',
        'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL',
    ),
    'filter' => array(
        'IBLOCK_ID' => $arParams['IBLOCK_ID'],
        "IBLOCK_SECTION_ID" => $arResult["SECTION"]["ID"],
    )
));
while ($arItem = $dbItems->fetch()){
    $arItem['DETAIL_PAGE_URL'] = CIBlock::ReplaceDetailUrl($arItem['DETAIL_PAGE_URL'], $arItem, false, 'E');
}
/*
Array
(
    [ID] => 666
    [IBLOCK_SECTION_ID] => 333
    [CODE] => italo_brown_60_120
    [NAME] => Italo Brown 60*120
    [IBLOCK_ID] => 1
    [DETAIL_PAGE_URL] => /catalog/plitka/italo/italo_brown_60_120/
)
*/