Блог разработчика 1С-Битрикс

Единственный активный элемент в инфоблоке: автодеактивация баннера в 1С-Битрикс

Иногда на сайте нужен небольшой, но «громкий» поп-ап, который появляется на каждой странице и сообщает о важной акции, доставке или cookie-политике.

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

Только один активный элемент в инфоблоке

Ниже - готовый код, который автоматически следит за этим правилом: если редактор включает новый элемент, скрипт сам отключает прежний. Администратору не нужно помнить про ручную деактивацию, а во фронтенде вы всегда получаете ровно один элемент для поп-апа.

<?php
/**
 * /local/php_interface/init.php
 * Гарантирует, что в инфоблоке SINGLE_ACTIVE_IBLOCK_ID активным остаётся ровно один элемент.
 * При активации нового баннера предыдущий автоматически деактивируется.
 */
use Bitrix\Main\EventManager;
use Bitrix\Iblock\ElementTable;
const SINGLE_ACTIVE_IBLOCK_ID = 1;   // ← подставьте ID своего инфоблока
$em = EventManager::getInstance();
$em->addEventHandler('iblock', 'OnAfterIBlockElementAdd',    'keepSingleActive');
$em->addEventHandler('iblock', 'OnAfterIBlockElementUpdate', 'keepSingleActive');

/**
 * Оставляет один активный элемент.
 *
 * @param array $arFields поля сохранённого элемента
 */
function keepSingleActive(array $arFields): void
{
    // интересуемся только нужным инфоблоком
    if ((int)$arFields['IBLOCK_ID'] !== SINGLE_ACTIVE_IBLOCK_ID) {
        return;
    }
    $currentId = (int)$arFields['ID'];
    // получаем актуальное состояние элемента
    $current = ElementTable::getById($currentId)->fetch();
    if (!$current || $current['ACTIVE'] !== 'Y') {      // сохраняют неактивный — выходим
        return;
    }
    // защита от рекурсии
    static $inProcess = false;
    if ($inProcess) {
        return;
    }
    $inProcess = true;
    // ищем все остальные активные элементы
    $cursor = ElementTable::getList([
        'select' => ['ID'],
        'filter' => [
            '=IBLOCK_ID' => SINGLE_ACTIVE_IBLOCK_ID,
            '=ACTIVE'    => 'Y',
            '!ID'        => $currentId,
        ],
    ]);
    $el = new CIBlockElement;
    while ($row = $cursor->fetch()) {
        // переводим найденные элементы в неактивные
        $el->Update($row['ID'], ['ACTIVE' => 'N']);
    }
    $inProcess = false;
}

Как работает скрипт

Шаг Что происходит
1. Подключение После загрузки ядра Bitrix код регистрирует события OnAfterIBlockElementAdd и OnAfterIBlockElementUpdate.
2. Проверка инфоблока Обработчик реагирует только на элементы инфоблока SINGLE_ACTIVE_IBLOCK_ID.
3. Проверка активности Если сохраняемый элемент неактивный, скрипт ничего не делает.
4. Защита от рекурсии Статическая переменная $inProcess блокирует повторный запуск обработчика, когда мы сами меняем ACTIVE у старого баннера.
5. Поиск активных ORM-запрос выбирает все другие активные элементы того же инфоблока.
6. Авто-деактивация Каждый найденный элемент переводится в ACTIVE = N методом $el->Update().
7. Финиш Новый баннер остаётся единственным активным, а сохранение завершается без блокировок и ошибок.

Вывод поп-апа во фронтенде

<?php
$arPopup = CIBlockElement::GetList(
    [],
    ['IBLOCK_ID' => SINGLE_ACTIVE_IBLOCK_ID, 'ACTIVE' => 'Y'],
    false,
    ['nTopCount' => 1],
    ['ID', 'NAME', 'PREVIEW_TEXT', 'PREVIEW_PICTURE', 'PROPERTY_LINK']
)->Fetch();
if ($arPopup) {
    ?>
    <div id="promo-popup" class="popup">
        <h3><?= htmlspecialcharsbx($arPopup['NAME']) ?></h3>
        <p><?= $arPopup['PREVIEW_TEXT'] ?></p>
        <? if ($arPopup['PROPERTY_LINK_VALUE']): ?>
            <a href="<?= $arPopup['PROPERTY_LINK_VALUE'] ?>" class="btn">Подробнее</a>
        <? endif; ?>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            document.getElementById('promo-popup').style.display = 'block';
        });
    </script>
    <?php
}

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

Полезные расширения

Идея Как реализовать
Ограничение по времени Добавьте в публичный фильтр ACTIVE_DATE => 'Y' и ставьте элементам ACTIVE_FROM/ACTIVE_TO.
Мультиязычность Храните тексты в множественных свойствах (PREVIEW_TEXT_EN, ) или заведите по инфоблоку на язык.
Несколько типов поп-апов Добавьте свойство «Тип» (cookies / promo / alert) и фильтруйте в обработчике и фронтенде по нему.
Включение по расписанию Используйте агенты или CRON-скрипт для автоматической активации нужного элемента ― обработчик сам выключит старый.

Итог

  • Администраторы больше не думают, какой баннер выключить — скрипт делает это автоматом.
  • Фронтенд всегда получает ровно один активный элемент, поп-ап не дублируется.
  • Производительность не падает: после сохранения выполняется лишь один лёгкий ORM-запрос + несколько апдейтов при необходимости.

Подключите файл в /local/php_interface/init.php — и ваш поп-ап всегда будет показывать актуальную информацию без риска дублирования.


Теги:  баннеры, автодеактивация, PHP, инфоблоки, поп-ап


Стоимость услуг по разработке и сопровождению сайтов на 1C-Битрикс

Аутсорсинг

готов помочь, если нет времени

договорная

Могу взять на себя работы по full-stack на основе готовой верстки

* если нет верстки, то возможность верстать с Figma в режиме редактора

Техническая поддержка

выполняется с сайтами на основе любых CMS

от 5 000 рублей
Оптимизация производительности действующих интернет-проектов, наполнение и сопровождение, полная техническая поддержка и продвижение в поисковых сетях.

* стоимость зависит от объема и сложности выполняемых работ, используемой CMS.

Участие в проекте

привлечение в проект на part-time основе

от 20 000 рублей / неделя

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

* сумма фиксированная