Разработка на 1С-Битрикс Михаил Базаров
Блог-note

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

Просмотров: 884
При разработке на 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С-Битрикс

Стоимость разработки сайта зависит от объёма и сложности проекта. Ниже приведены ориентировочные цены, как правило не выходят за обозначенные рамки. Срок разработки зависит от сложности проекта: как правило называю сроки с запасом.

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

Включено в стоимость разработки

В цену уже заложено всё, что нужно для легального и быстрого запуска. Доплачивать за базовые вещи не придётся.

  • Лицензия и модули

    Лицензия на «1С-Битрикс» нужной редакции, дополнительные модули и видео-инструкции по работе с готовым проектом.

  • Скорость и SEO

    Оптимизация кода и сервера под быструю загрузку, базовая SEO-оптимизация и добавление сайта в поисковые системы.

Блог-note

Заметки по 1С-Битрикс