Если нужно подключить инфоблок в своем файле Битрикс, а не через готовый компонент новостей или каталога, сразу возникают вопросы: как правильно проинициализировать модуль, где взять 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 - так проще тестировать и переиспользовать код на других страницах.