Все новости

Автоматическое снятие меток "Новинка" в Bitrix: два подхода

⚡ Bitrix 5 мин на чтение

В интернет-магазинах на 1С-Битрикс часто возникает задача регулярно очищать метки "Новинка" у товаров. Рассмотрим два готовых решения для агентов Bitrix, которые автоматически снимают нужные флажки раз в 3 месяца.

Задача

У товаров в инфоблоке есть несколько способов отметки "Новинка":

  1. Свойство OFFERS — флажок "Новинка" в списке предложений
  2. Свойство NEW — значение "Да", отдельная метка на товаре
  3. Свойство CATEGORY — значение "Новое", отвечающее за вывод в блок "Новинки" на главной странице

Первая задача — снимать флажок "Новинка" у свойства OFFERS. Вторая — комплексная очистка свойств NEW и CATEGORY, что используется в линейке решений от Intec, где разделяются понятия метки на товаре (NEW = Да) и попадания в список новинок на главной (CATEGORY = Новое).

Оба решения поддерживают работу с несколькими инфоблоками — достаточно указать их ID в массиве.

Решение 1: Снятие флажка "Новинка" в свойстве OFFERS

Файл local/php_interface/include/uncheck_offers_agent.php:

<?php

class UncheckOffersAgent
{
    const IBLOCK_IDS = [16, 17];
    const PROPERTY_CODE = 'OFFERS';
    const VALUE_TO_REMOVE = 'Новинка';

    public static function run(): string
    {
        CModule::IncludeModule('iblock');

        foreach (self::IBLOCK_IDS as $iblockId) {
            $targetEnumId = self::getTargetEnumId($iblockId);
            if (!$targetEnumId) {
                AddMessage2Log("UncheckOffersAgent: значение 'Новинка' не найдено в свойстве OFFERS для ИБ {$iblockId}", 'main');
                continue;
            }

            $rsElements = CIBlockElement::GetList(
                ['ID' => 'ASC'],
                ['IBLOCK_ID' => $iblockId],
                false,
                false,
                ['ID', 'NAME']
            );

            while ($element = $rsElements->GetNext()) {
                $currentValues = [];
                $rsProps = CIBlockElement::GetProperty(
                    $iblockId,
                    $element['ID'],
                    [],
                    ['CODE' => self::PROPERTY_CODE]
                );
                while ($prop = $rsProps->GetNext()) {
                    $currentValues[] = $prop['VALUE_ENUM_ID'];
                }

                if (in_array($targetEnumId, $currentValues)) {
                    $newValues = array_diff($currentValues, [$targetEnumId]);
                    CIBlockElement::SetPropertyValuesEx(
                        $element['ID'],
                        $iblockId,
                        [self::PROPERTY_CODE => array_values($newValues)]
                    );
                }
            }
        }

        return self::class . '::run();';
    }

    private static function getTargetEnumId(int $iblockId): ?int
    {
        $propertyEnums = CIBlockPropertyEnum::GetList([], [
            'IBLOCK_ID' => $iblockId,
            'CODE' => self::PROPERTY_CODE,
        ]);

        while ($enum = $propertyEnums->GetNext()) {
            if (mb_stripos($enum['VALUE'], self::VALUE_TO_REMOVE) !== false) {
                return (int)$enum['ID'];
            }
        }

        return null;
    }
}

Решение 2: Снятие флажков NEW и CATEGORY

Этот подход используется в решениях Intec: товар может одновременно иметь метку "Новинка" (свойство NEW = Да) и состоять в категории "Новинки" для вывода на главной (свойство CATEGORY = Новое). При регулярной очистке снимаются оба флажка одновременно.

Файл local/php_interface/include/clear_new_flags_agent.php:

<?php

class ClearNewFlagsAgent
{
    const IBLOCK_IDS = [16, 17];

    const PROPERTIES = [
        'CATEGORY' => 'Новое',
        'NEW'      => 'Да',
    ];

    public static function run(): string
    {
        CModule::IncludeModule('iblock');

        foreach (self::IBLOCK_IDS as $iblockId) {
            foreach (self::PROPERTIES as $code => $valueToRemove) {
                self::uncheckValue($iblockId, $code, $valueToRemove);
            }
        }

        return self::class . '::run();';
    }

    private static function uncheckValue(int $iblockId, string $propertyCode, string $valueToRemove): void
    {
        $targetEnumId = self::getTargetEnumId($iblockId, $propertyCode, $valueToRemove);
        if (!$targetEnumId) {
            AddMessage2Log(
                "ClearNewFlagsAgent: значение '{$valueToRemove}' не найдено в свойстве {$propertyCode} для ИБ {$iblockId}",
                'main'
            );
            return;
        }

        $rsElements = CIBlockElement::GetList(
            ['ID' => 'ASC'],
            ['IBLOCK_ID' => $iblockId],
            false,
            false,
            ['ID', 'NAME']
        );

        while ($element = $rsElements->GetNext()) {
            $currentValues = [];
            $rsProps = CIBlockElement::GetProperty(
                $iblockId,
                $element['ID'],
                [],
                ['CODE' => $propertyCode]
            );
            while ($prop = $rsProps->GetNext()) {
                $currentValues[] = $prop['VALUE_ENUM_ID'];
            }

            if (in_array($targetEnumId, $currentValues)) {
                $newValues = array_diff($currentValues, [$targetEnumId]);
                CIBlockElement::SetPropertyValuesEx(
                    $element['ID'],
                    $iblockId,
                    [$propertyCode => array_values($newValues)]
                );
            }
        }
    }

    private static function getTargetEnumId(int $iblockId, string $propertyCode, string $valueToRemove): ?int
    {
        $propertyEnums = CIBlockPropertyEnum::GetList([], [
            'IBLOCK_ID' => $iblockId,
            'CODE' => $propertyCode,
        ]);

        while ($enum = $propertyEnums->GetNext()) {
            if (mb_stripos($enum['VALUE'], $valueToRemove) !== false) {
                return (int)$enum['ID'];
            }
        }

        return null;
    }
}

Подключение и регистрация агентов

Файл local/php_interface/init.php — автозагрузка классов:

<?php
require_once __DIR__ . '/include/uncheck_offers_agent.php';
require_once __DIR__ . '/include/clear_new_flags_agent.php';

Регистрация агентов на 90 дней (запустить однократно):

<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

$interval = 90 * 24 * 60 * 60; // 7776000 секунд

CAgent::AddAgent(
    'UncheckOffersAgent::run();',
    '',
    'N',
    $interval,
    '',
    'Y',
    '',
    100
);

CAgent::AddAgent(
    'ClearNewFlagsAgent::run();',
    '',
    'N',
    $interval,
    '',
    'Y',
    '',
    100
);

Как это работает

  1. Агент проходит по всем указанным ID инфоблоков
  2. Для каждого инфоблока находит ID значения списка (например, "Новинка" в свойстве OFFERS)
  3. Перебирает все элементы инфоблока, проверяя, установлен ли нужный флажок
  4. Если флажок установлен — удаляет его из множества значений свойства и сохраняет

Оба агента возвращают строку с именем своего метода (ClassName::run();), благодаря чему Bitrix автоматически перезапускает их через заданный интервал.

Настройка под свой проект

Достаточно изменить константы в начале каждого класса:

  • IBLOCK_IDS — массив ID инфоблоков, в которых нужно снимать флажки
  • Для UncheckOffersAgent: PROPERTY_CODE и VALUE_TO_REMOVE — код свойства и значение для поиска
  • Для ClearNewFlagsAgent: массив PROPERTIES — пары код свойства => значение для удаления