GetNextElement() или GetNext() — какой метод выбрать при работе с инфоблоками

Просмотров: 117

При разработке на 1С-Битрикс один из частых вопросов — какой метод использовать для обхода результатов выборки из инфоблока: GetNext() или GetNextElement()?

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

В этой статье разберу оба подхода и объясню, почему в большинстве случаев предпочитаю второй вариант.

Про устаревшие методы и 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_* — работает быстро и избавляет от необходимости помнить структуру инфоблока.

Если остались вопросы — пишите в комментариях.

Стоимость разработки на 1С-Битрикс:

Индивидуальная разработка магазина

от 500 000 руб. от 5-ти недель

Разработка магазина на 1С-Битрикс с нуля. Дизайн, сборка и оптимизация производительности под конкретный проект и требования. Реализация любого функционала без ограничений готовых решений.

Запуск сайта на готовом решении

от 100 000 руб. от 7-ми дней

Вариант для тех, кто не хочет тратить много средств на индивидуальный проект, и не имеет серьезных требований к сайту. Магазин, быстро запускается на базе одного из 200-та готовых решений.

Мобильное приложение

от 500 000 руб. от 1-го месяца

Разработка кроссплатформенного мобильного приложения, которое не уступает нативным решениям как в производительности, так и пользовательском опыте. Публикуется в AppStore, GooglePlay и RuStore

Сайт компании

от 350 000 руб. от 1-го месяца

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

Инфресурс

от 400 000 руб. от 5-ти недель

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

Лечение сайтов от вирусов

от 40 000 руб. от 2-х дней

Выполню полную проверку сайта и окружения. В случае обнаружения вирусов проведу полный комплекс лечения проекта и закрытия лазеек.