1С Битрикс · 7 мин чтения

Как подключить инфоблок в своём файле Битрикс на PHP

Если нужно подключить инфоблок в своем файле Битрикс, а не через готовый компонент новостей или каталога, сразу возникают вопросы: как правильно проинициализировать модуль, где взять ID инфоблока и почему CIBlockElement::GetList иногда возвращает пустой результат вместо ожидаемых товаров. На практике я вывожу элементы инфоблока в кастомных PHP-файлах, когда нужен нестандартный дизайн блока, выборка для AJAX-обработчика или интеграция с внешним сервисом вроде СДЭК или платёжного шлюза. Ниже - рабочий код через старый API и через D7-классы, с фильтрами, свойствами и кешированием, который я использую на реальных проектах.

Зачем выводить элементы инфоблока в своём файле, а не через компонент

Штатные компоненты bitrix:news.list и bitrix:catalog.section закрывают большинство типовых задач: листинг новостей, карточки товаров, детальные страницы. Но у них есть свои шаблоны, свои кеш-параметры и логика, которая иногда мешает, а не помогает.

Отдельный PHP-файл с прямым обращением к API инфоблоков нужен мне в таких случаях:

  • Вывод элементов в нестандартном месте страницы - например, три случайных товара в футере или блок «с этим товаром покупают» внутри чужого шаблона.
  • AJAX-обработчик, который отдаёт JSON с элементами инфоблока для фронтенда на React или Vue без перерисовки всей страницы.
  • Интеграция с внешним сервисом: выгрузка остатков в 1С, отправка каталога в Telegram-бота на aiogram, синхронизация цен с маркетплейсом или сценарий в n8n.
  • Кастомная выборка, которую сложно собрать через параметры компонента - например, элементы сразу из нескольких инфоблоков с объединением и сортировкой на стороне PHP.

Для интернет-магазина с каталогом в несколько тысяч товаров я обычно делаю отдельный файл вывода для мобильной версии карточки, а не тяну туда весь шаблон catalog.section - так проще контролировать вёрстку и скорость отдачи страницы. Такую разработку интернет-магазина под ключ на Битриксе я беру в работу как отдельный проект, а не разовую правку шаблона.

Подключаю модуль iblock и нахожу ID нужного инфоблока

Прежде чем обращаться к CIBlockElement или D7-классам, нужно поднять ядро Битрикса и подключить модуль iblock. Если файл открывается напрямую, не через компонент, в начале подключаю prolog_before.php - он инициализирует ядро без визуального шаблона сайта:

<?php
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

use Bitrix\Main\Loader;

if (!Loader::includeModule("iblock")) {
    die("Модуль iblock не установлен");
}

Если файл должен отдавать страницу целиком, с шапкой и подвалом сайта, вместо prolog_before.php подключаю /bitrix/header.php в начале и /bitrix/footer.php в конце - тогда работает и шаблон сайта, и меню, и остальные компоненты на странице.

ID инфоблока можно посмотреть в админке, в разделе с контентом, но в коде надёжнее искать инфоблок по символьному коду через IblockTable - тогда при переносе на тестовый сервер или при другом ID в копии базы код не ломается:

<?php
use Bitrix\Iblock\IblockTable;

$iblock = IblockTable::getList([
    "select" => ["ID"],
    "filter" => [
        "CODE" => "catalog",
        "IBLOCK_TYPE_ID" => "catalog",
    ],
])->fetch();

$iblockId = (int)$iblock["ID"];

Вывожу элементы через CIBlockElement::GetList

Это классический способ, он работает в любой версии Битрикса и остаётся самым понятным для простых выборок. Третий параметр GetList - это группировка, arGroupBy: со значением false группировки нет, а пустой массив вместо неё заставит метод вернуть только количество элементов CNT по фильтру. Четвёртый, arNavStartParams, отвечает за постраничную навигацию и ограничение количества, пятый, arSelectFields, - за список полей, которые реально нужны в шаблоне. Если не ограничить выборку конкретными полями, метод по умолчанию тянет все поля, включая детальный текст, и на большом каталоге это заметно замедляет страницу.

<?php
$arFilter = [
    "IBLOCK_ID" => $iblockId,
    "ACTIVE" => "Y",
];

$res = \CIBlockElement::GetList(
    ["SORT" => "ASC"],
    $arFilter,
    false,
    false,
    ["ID", "IBLOCK_ID", "NAME", "PREVIEW_TEXT", "DETAIL_PAGE_URL", "PROPERTY_PRICE"]
);

while ($ob = $res->GetNextElement()) {
    $arFields = $ob->GetFields();
    $arProps = $ob->GetProperties();

    echo $arFields["NAME"]." - ".$arProps["PRICE"]["VALUE"]."<br>";
}

Свойство PROPERTY_PRICE в массиве select - это подсказка ядру подтянуть значение свойства с кодом PRICE прямо в основную выборку, без отдельного вызова GetProperties для каждого элемента. Вместе с ним в select обязательно должны быть IBLOCK_ID и ID, иначе выборка работает некорректно, а само значение приходит в поле PROPERTY_PRICE_VALUE. Работает не для всех типов свойств: множественные лучше забирать отдельным запросом, об этом дальше.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Современный способ: скомпилированный ORM-класс инфоблока

С переходом на D7-стиль в Битриксе появился ORM-доступ к инфоблокам, но готовые классы Bitrix\Iblock\ElementTable и Bitrix\Iblock\SectionTable документация использовать запрещает: они работают только с базовыми полями. Штатный путь другой - один раз скомпилировать ORM-класс конкретного инфоблока по его символьному коду API и дальше обращаться к нему. Синтаксис ближе к обычным ORM-запросам, а результат приходит без обёртки вида GetNextElement/GetFields:

<?php
\Bitrix\Iblock\IblockTable::compileEntity("News");
$elementNewsClass = \Bitrix\Iblock\Elements\ElementNewsTable::class;

$news = $elementNewsClass::query()
    ->setSelect(["ID", "NAME", "PREVIEW_TEXT"])
    ->where("ACTIVE", "Y")
    ->setOrder(["SORT" => "ASC"])
    ->fetchCollection();

foreach ($news as $item) {
    echo $item->getName();
}

Для несложных выборок разницы почти нет, разница чувствуется на более сложных сценариях:

Критерий CIBlockElement::GetList ElementTable::getList
Стиль работы с результатом GetNextElement + GetFields в цикле fetch/fetchAll без промежуточных объектов
Свойства элемента Отдельный вызов GetProperties или GetProperty Через связку с PropertyTable или отдельный запрос
Когда беру в работу Быстрая простая выборка, старый код проекта Сложные фильтры, переиспользуемые классы, новый код

CIBlockElement::GetList я по-прежнему использую для быстрых задач и там, где в проекте уже так сделано - переписывать рабочий код без причины смысла не вижу.

Свойства, разделы и фильтры при выводе элементов

Когда у элемента несколько свойств или свойство множественное, например галерея фото или список размеров, значения одиночных свойств дешевле получить прямо в основной выборке: для этого в select и указывают IBLOCK_ID, ID и PROPERTY_ с кодом свойства в верхнем регистре. Отдельный метод CIBlockElement::GetProperty берёт значения для одного элемента за вызов, поэтому по списку элементов он идёт циклом:

<?php
$elementIds = [101, 102, 103];

foreach ($elementIds as $elementId) {
    $dbProps = \CIBlockElement::GetProperty(
        $iblockId,
        $elementId,
        ["sort" => "asc"],
        ["CODE" => "PRICE"]
    );

    while ($prop = $dbProps->Fetch()) {
        echo $elementId.": ".$prop["VALUE"];
    }
}

Для вывода элементов из конкретного раздела каталога добавляю в фильтр SECTION_ID, а если нужны элементы из вложенных разделов тоже - INCLUDE_SUBSECTIONS со значением «Y»:

<?php
$arFilter = [
    "IBLOCK_ID" => $iblockId,
    "SECTION_ID" => 15,
    "INCLUDE_SUBSECTIONS" => "Y",
    "ACTIVE" => "Y",
    "ACTIVE_DATE" => "Y",
];

ACTIVE_DATE со значением «Y» отсекает элементы с истёкшим или ещё не наступившим сроком активности - удобно для акционных товаров с ограниченным сроком показа. На сайтах с несколькими доменами в фильтр стоит добавлять и проверку по SITE_ID, если инфоблок общий для всех сайтов группы, иначе на втором домене всплывут элементы, которые к нему не относятся.

Кеширование вывода и типичные ошибки при подключении инфоблока

Прямой вызов CIBlockElement::GetList не кеширует результат сам по себе, в отличие от компонента, где кеш настраивается параметром CACHE_TIME. Для собственного файла кеш собираю вручную через Bitrix\Main\Data\Cache:

<?php
use Bitrix\Main\Data\Cache;

$cache = Cache::createInstance();
$cacheId = "iblock_".$iblockId."_active_elements";
$cacheDir = "/iblock/".$iblockId."/";

if ($cache->initCache(3600, $cacheId, $cacheDir)) {
    $items = $cache->getVars();
} elseif ($cache->startDataCache()) {
    $items = [];

    $res = \CIBlockElement::GetList(
        ["SORT" => "ASC"],
        ["IBLOCK_ID" => $iblockId, "ACTIVE" => "Y"],
        false,
        false,
        ["ID", "NAME", "DETAIL_PAGE_URL"]
    );

    while ($ob = $res->GetNextElement()) {
        $items[] = $ob->GetFields();
    }

    $cache->endDataCache($items);
}

foreach ($items as $item) {
    echo $item["NAME"];
}

3600 секунд в примере - это час, для каталога с частым изменением цен я обычно ставлю кеш короче, на 10-15 минут, а принудительный сброс вешаю на события изменения элемента инфоблока.

Ошибки, с которыми сталкивался на реальных проектах чаще всего:

  • Не проверили результат Loader::includeModule(“iblock”) - если модуль не подключён, класс CIBlockElement просто не существует, и PHP падает с фатальной ошибкой вместо понятного сообщения.
  • ID инфоблока захардкожен и совпадает только на боевом сервере - на тестовом окружении с другой базой выборка тихо возвращает пустоту.
  • Забыли ACTIVE => “Y” в фильтре - в выводе всплывают товары, снятые с публикации.
  • Выбирают все поля без ограничения через select - на инфоблоке в 10-15 тысяч элементов такой запрос заметно нагружает базу.
  • Подключают prolog_before.php в файле, который сам вызывается из уже поднятого окружения Битрикса, например из компонента - получают повторную инициализацию и странные побочные эффекты.

1С-Битрикс и PHP регулярно обновляются, поэтому перед внедрением я сверяюсь с актуальной документацией.

Частые вопросы

Можно ли выводить элементы инфоблока без prolog_before.php?

Можно, если файл подключается изнутри уже работающего окружения Битрикса - например, вызывается из компонента, из обработчика события или из другого файла, где ядро уже поднято. Если же файл открывается напрямую по прямой ссылке, ядро и модули нужно поднять явно, иначе классы вроде CIBlockElement или Bitrix\Iblock\ElementTable будут не определены.

Чем CIBlockElement отличается от ElementTable на практике?

CIBlockElement - старый API, доступен с ранних версий Битрикса, работает через GetNextElement, GetFields и GetProperties. ElementTable - часть D7 ORM, возвращает данные через fetch и fetchAll и лучше сочетается с остальными классами уровня Bitrix\Main\ORM. Для простого вывода списка разницы в результате нет, я выбираю то, что уже используется в конкретном проекте, чтобы не смешивать два стиля в одном модуле.

Как ускорить вывод большого каталога через инфоблок?

Ограничиваю select только нужными полями, забираю множественные свойства отдельным запросом на группу элементов вместо цикла с GetProperties, добавляю постраничную навигацию вместо выгрузки всего каталога разом и кеширую результат через Bitrix\Main\Data\Cache на срок, который соответствует тому, как часто меняются данные.

Подойдёт этот код для шаблона компонента?

Да, тот же CIBlockElement::GetList или ElementTable можно вызывать прямо в template.php или в result_modifier.php компонента - ядро там уже поднято. Но для логики выборки данных лучше отдельный класс или файл, а в шаблоне оставлять только вывод HTML - так проще тестировать и переиспользовать код на других страницах.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно