На первый взгляд они делают одно и то же, но на практике между ними есть существенная разница, которая влияет как на производительность, так и на удобство дальнейшей поддержки кода.
В этой статье разберу оба подхода и объясню, почему в большинстве случаев предпочитаю второй вариант.
Про устаревшие методы и D7
Да, CIBlockElement::GetList() — это старое API, и у Битрикса давно есть более современный подход через
D7 (\Bitrix\Iblock\Elements). Однако на практике переход на D7 для работы с инфоблоками не всегда
оправдан:
- D7 API для инфоблоков требует предварительной настройки (генерация классов через административный интерфейс)
- Синтаксис сложнее, порог входа выше
- По производительности старое API практически не уступает D7 при грамотном использовании
- Огромное количество существующего кода написано на старом API — его нужно уметь читать и поддерживать
Поэтому GetNext() и GetNextElement() по-прежнему актуальны и активно используются в
проектах. Если же вам принципиально важно работать через D7 — в этой
статье разбираю работу с инфоблоками через новое API.
Главное: GetNext() возвращает плоский массив, но требует явного перечисления всех
нужных свойств в arSelect. Если забыли указать PROPERTY_PRICE — цену не получите. GetNextElement()
возвращает объект, через который можно получить абсолютно все свойства элемента, даже те, о которых вы изначально не
подумали.
Метод GetNext()
GetNext() возвращает ассоциативный массив с данными элемента. Всё просто и понятно, однако есть важный
нюанс: вы получаете только те поля и свойства, которые явно указали в параметре arSelect:
<?php
$arSelect = ['ID', 'NAME', 'PROPERTY_PRICE', 'PROPERTY_CITY'];
$arFilter = ['IBLOCK_ID' => 4, 'ACTIVE' => 'Y'];
$rsElements = CIBlockElement::GetList(
['SORT' => 'ASC'],
$arFilter,
false,
false,
$arSelect
);
while ($arElement = $rsElements->GetNext()) {
echo $arElement['ID'] . ' - ' . $arElement['NAME'] . '<br>';
echo 'Цена: ' . $arElement['PROPERTY_PRICE_VALUE'] . '<br>';
echo 'Город: ' . $arElement['PROPERTY_CITY_VALUE'] . '<br>';
}
?>
Основная сложность при работе с GetNext() — необходимость помнить и явно указывать каждое свойство.
Типичная ситуация: инфоблок содержит 30 свойств, при первоначальной разработке указали 5 необходимых. Через какое-то
время появляется задача вывести дополнительное поле — приходится возвращаться в код и добавлять PROPERTY_ADDRESS
в arSelect. Потом ещё одно свойство, и ещё... Код обрастает правками, а вероятность что-то упустить
возрастает.
- Плоский массив — удобный доступ к данным, всё на одном уровне
- Быстрое обращение — данные сразу готовы к использованию
- Недостаток: необходимо заранее знать и явно указывать все нужные свойства
- Ещё недостаток: множественные свойства обрабатываются непредсказуемо
Из практики: немало времени было потрачено на поиск причины, почему данные не приходят — а дело
было в забытом свойстве в arSelect.
Метод GetNextElement()
GetNextElement() возвращает объект с двумя полезными методами:
GetFields()— возвращает основные поля элемента (ID, NAME, CODE и т.д.)GetProperties()— возвращает все свойства элемента со всей мета-информацией
<?php
$arSelect = ['ID', 'NAME', 'CODE', 'DETAIL_PAGE_URL', 'PROPERTY_*'];
$arFilter = ['IBLOCK_ID' => 4, 'ACTIVE' => 'Y'];
$rsElements = CIBlockElement::GetList(
['SORT' => 'ASC'],
$arFilter,
false,
false,
$arSelect
);
while ($obElement = $rsElements->GetNextElement()) {
// Получаем поля
$arFields = $obElement->GetFields();
// Получаем ВСЕ свойства
$arProps = $obElement->GetProperties();
echo $arFields['ID'] . ' - ' . $arFields['NAME'] . '<br>';
echo 'URL: ' . $arFields['DETAIL_PAGE_URL'] . '<br>';
// Свойства доступны со всей дополнительной информацией
if (isset($arProps['PRICE'])) {
echo 'Цена: ' . $arProps['PRICE']['VALUE'] . '<br>';
echo 'Тип свойства: ' . $arProps['PRICE']['PROPERTY_TYPE'] . '<br>';
}
}
?>
При использовании GetNextElement() с PROPERTY_* отпадает необходимость держать в голове
весь список свойств инфоблока. Появилась новая задача — вывести дополнительное свойство? Просто обращаетесь к нему в
$arProps — оно уже загружено.
- Возвращает объект — немного сложнее в обращении, но значительно гибче
- Все свойства сразу — не нужно ничего дописывать в выборку
- Полная информация — доступны тип свойства, код, ID, признак множественности
- Множественные свойства — обрабатываются предсказуемо и единообразно
Важно: роль PROPERTY_* в производительности
Этот момент не всегда очевиден. При использовании GetNextElement() без указания PROPERTY_*
в arSelect свойства будут загружаться дополнительными запросами к базе данных. То есть на каждый
элемент — отдельный запрос. При выборке 100 элементов это 100 лишних обращений к БД.
Правило: при использованииGetNextElement()всегда указывайтеPROPERTY_*вarSelect
<?php
// Правильно — все свойства загружаются одним запросом
$arSelect = ['ID', 'NAME', 'CODE', 'PROPERTY_*'];
$rsElements = CIBlockElement::GetList(
['SORT' => 'ASC'],
$arFilter,
false,
false,
$arSelect
);
while ($obElement = $rsElements->GetNextElement()) {
$arFields = $obElement->GetFields();
$arProps = $obElement->GetProperties(); // Данные уже в памяти
}
?>
<?php
// Неправильно — дополнительные запросы на каждый элемент
$arSelect = ['ID', 'NAME', 'CODE']; // Нет PROPERTY_*
while ($obElement = $rsElements->GetNextElement()) {
$arProps = $obElement->GetProperties(); // Запрос к БД на каждый вызов
}
?>
Для простых случаев, когда набор полей известен заранее и не планирует расширяться, GetNext() вполне
подходит:
<?php
// Фиксированный набор полей
$arSelect = ['ID', 'NAME', 'ACTIVE_FROM', 'PREVIEW_TEXT'];
$arFilter = ['IBLOCK_ID' => 1, 'ACTIVE' => 'Y'];
$rsNews = CIBlockElement::GetList(
['ACTIVE_FROM' => 'DESC'],
$arFilter,
false,
['nTopCount' => 10],
$arSelect
);
while ($arNews = $rsNews->GetNext()) {
echo '<h3>' . $arNews['NAME'] . '</h3>';
echo '<p>' . $arNews['PREVIEW_TEXT'] . '</p>';
echo '<span>' . $arNews['ACTIVE_FROM'] . '</span>';
}
?>
При работе со сложными сущностями (например, рестораны с координатами, адресом, телефоном, графиком работы, рейтингом
и десятком других характеристик) GetNextElement() существенно упрощает жизнь:
<?php
$arSelect = ['ID', 'NAME', 'CODE', 'DETAIL_PAGE_URL', 'PROPERTY_*'];
$arFilter = ['IBLOCK_ID' => 4, 'ACTIVE' => 'Y'];
$rsRestaurants = CIBlockElement::GetList(
['NAME' => 'ASC'],
$arFilter,
false,
false,
$arSelect
);
$arResult = [];
while ($obRestaurant = $rsRestaurants->GetNextElement()) {
$arFields = $obRestaurant->GetFields();
$arProps = $obRestaurant->GetProperties();
// Все свойства доступны без дополнительных правок в выборке
$arResult[] = [
'ID' => $arFields['ID'],
'NAME' => $arFields['NAME'],
'URL' => $arFields['DETAIL_PAGE_URL'],
'LAT' => $arProps['LAT']['VALUE'],
'LON' => $arProps['LON']['VALUE'],
'ADDRESS' => $arProps['ADDRESS']['VALUE'],
'PHONE' => $arProps['PHONE']['VALUE'],
'WORK_TIME' => $arProps['WORK_TIME']['VALUE'],
'RATING' => $arProps['RATING']['VALUE'],
// Добавление нового свойства — просто строка кода
];
}
?>
При работе с множественными свойствами (галереи изображений, теги, связанные элементы) GetNext() создаёт
больше проблем, чем решает:
<?php
$arSelect = ['ID', 'NAME', 'PROPERTY_*'];
$arFilter = ['IBLOCK_ID' => 5, 'ACTIVE' => 'Y'];
$rsProducts = CIBlockElement::GetList(
['SORT' => 'ASC'],
$arFilter,
false,
false,
$arSelect
);
while ($obProduct = $rsProducts->GetNextElement()) {
$arFields = $obProduct->GetFields();
$arProps = $obProduct->GetProperties();
echo '<h3>' . $arFields['NAME'] . '</h3>';
// Множественное свойство обрабатывается единообразно
if (isset($arProps['PHOTOS']) && $arProps['PHOTOS']['MULTIPLE'] === 'Y') {
echo '<div class="gallery">';
foreach ($arProps['PHOTOS']['VALUE'] as $photoId) {
$arPhoto = CFile::GetFileArray($photoId);
echo '<img src="' . $arPhoto['SRC'] . '" alt="">';
}
echo '</div>';
}
// Свойство типа «список» — доступно и значение, и его описание
if (isset($arProps['COLOR'])) {
echo 'Цвет: ' . $arProps['COLOR']['VALUE_ENUM'] . '<br>';
}
}
?>
Вопрос производительности
Распространённое мнение: «GetNext() быстрее»:
- GetNext() — быстрее на 5-10% при выборке только базовых полей
- GetNextElement() с PROPERTY_* — практически сравнимая скорость при работе со свойствами
- GetNextElement() без PROPERTY_* — значительное падение производительности из-за дополнительных запросов
При правильном использовании GetNextElement() (с указанием PROPERTY_*) разница в скорости
минимальна, а преимущества в удобстве и поддерживаемости кода — существенны.
Рекомендация: не стоит оптимизировать то, что не является узким местом. Если есть сомнения в том,
какие свойства понадобятся — выбирайте GetNextElement().
Типичные ошибки
<?php
// НЕ ПРАВИЛЬНО Каждый вызов GetProperties() обращается к БД
$arSelect = ['ID', 'NAME'];
// ПРАВИЛЬНО Все свойства загружаются сразу
$arSelect = ['ID', 'NAME', 'PROPERTY_*'];
?>
PROPERTY_PHOTOS_VALUE может вернуть строку или массив в зависимости от количества значений. С GetNextElement()
поведение предсказуемо: проверяем $arProps['PHOTOS']['MULTIPLE'] и точно знаем, что получим.
В большинстве проектов использую GetNextElement() с PROPERTY_*. Причины:
- Нет необходимости запоминать все свойства инфоблока
- Не требуется править код при добавлении вывода нового свойства
- Множественные свойства обрабатываются корректно
- Производительность при правильном использовании практически идентична
GetNext() оправдан в случаях, когда нужны буквально 2-3 поля без свойств и точно известно, что набор
данных не будет расширяться.
Универсальный подход:GetNextElement()+PROPERTY_*— работает быстро и избавляет от необходимости помнить структуру инфоблока.
Если остались вопросы — пишите в комментариях.