Получаем информацию о скидке. Старый способ
Для начала, создаем скидку с интевалом в модуле маркетинга и выводим всю информацию о ней, в карточке товара:
$PRODUCT_ID = $arResult['ID'];
$dbProductDiscounts = CCatalogDiscount::GetList(
array("SORT" => "ASC"),
array(
"+PRODUCT_ID" => $PRODUCT_ID,
"ACTIVE" => "Y",
),
false,
false,
array(
"ID",
"SITE_ID",
"ACTIVE",
"ACTIVE_FROM",
"ACTIVE_TO",
"RENEWAL",
"NAME",
"SORT",
"MAX_DISCOUNT",
"VALUE_TYPE",
"VALUE",
"CURRENCY"
)
);
while ($arProductDiscounts = $dbProductDiscounts->Fetch())
{
print_r($arProductDiscounts)
}
В массиве arProductDiscounts - распечатается вся доступная информация о скидке. Но нам нужна только дата завершения- ACTIVE_TO. Эту дату сразу загоняем в переменную $saleDateTo. Обратите внимание, что если скидка уже осталась в прошлом- дата выведется в любом случае. Что бы этого не происходило, в фильтре оставляем отбор по активности "ACTIVE" => "Y" (флаг активности у прошедших скидок,снимается автоматически).
$dbProductDiscounts = CCatalogDiscount::GetList(
array("SORT" => "ASC"),
array(
"+PRODUCT_ID" => $arResult['ID'],
"ACTIVE" => "Y"
),
false,
false,
array(
"ACTIVE_TO"
)
);
while ($arProductDiscounts = $dbProductDiscounts->Fetch()) {
$saleDateTo = $arProductDiscounts['ACTIVE_TO'];
}
Собственно, если нужно просто вывести дату. Выводим переменную $saleDateTo в нужном месте шаблона. Более актуально, вывести количество дней до конца скидки. При этом, если осталось меньше одного дня, выводит "закончится сегодня".
Просто высчитываем разницу между текущей датой и датой завершения активности скидки. Подсказки в комментариях:
$diffSaleToDate = strtotime($saleDateTo) - strtotime(date("d.m.Y h:m:s"));
$saleEndDiff = floor($diffSaleToDate / 86000);
if ($saleEndDiff > 1) {
$daysToEndSale = 'Закончится через ' . $saleEndDiff . ' дн.';
} elseif ($saleEndDiff = 1) {
$daysToEndSale = 'Закончится сегодня';
} else {
unset($saleEndDiff);
}
Работа со скидками через D7 \Bitrix\Catalog\DiscountTable
Если нужно вывести информацию о скидке в карточке товара, раньше для этого использовали метод CCatalogDiscount::GetList, но сейчас правильнее работать через D7 - \Bitrix\Catalog\DiscountTable. Он даёт тот же результат, но работает через ORM, поддерживает кеширование и не помечен как устаревший.
Самое актуальное - вывести дату окончания скидки или количество дней до её завершения. Чтобы покупатель видел: "Успевай, скидка скоро сгорит".
Получаем информацию о скидке
Создаём скидку с интервалом в модуле "Маркетинг" и выводим информацию о ней в карточке товара через D7:
<?
use Bitrix\Catalog\DiscountTable;
use Bitrix\Main\Loader;
$PRODUCT_ID = $arResult['ID'];
if (!Loader::includeModule('catalog')) {
return;
}
$discountIterator = DiscountTable::getList([
'filter' => [
'=ACTIVE' => 'Y',
'=%SITE_ID' => SITE_ID,
[
'LOGIC' => 'OR',
['=PRODUCT_ID' => $PRODUCT_ID],
['=PRODUCT_ID' => false], // скидка на всю корзину
],
],
'select' => [
'ID', 'NAME', 'ACTIVE_FROM', 'ACTIVE_TO',
'VALUE_TYPE', 'VALUE', 'CURRENCY', 'MAX_DISCOUNT',
],
'order' => ['SORT' => 'ASC'],
]);
while ($discount = $discountIterator->fetch()) {
print_r($discount);
}
?>
В массиве $discount - вся информация о скидке. Нам нужна только дата завершения - ACTIVE_TO. Загоняем её в переменную $saleDateTo.
Обратите внимание: в D7 дата приходит уже объектом Bitrix\Main\Type\DateTime, а не строкой, как в старом API. Это удобнее - можно сразу сравнивать, форматировать и конвертировать без костылей.
Если скидка уже прошла - флаг ACTIVE снимается автоматически, поэтому в выдачу она не попадёт. Дополнительно можно отфильтровать по сайту.
<?
$discountIterator = DiscountTable::getList([
'filter' => [
'=ACTIVE' => 'Y',
'=%SITE_ID' => SITE_ID,
['=PRODUCT_ID' => $arResult['ID']],
],
'select' => ['ACTIVE_TO'],
'order' => ['SORT' => 'ASC'],
'limit' => 1,
]);
if ($discount = $discountIterator->fetch()) {
$saleDateTo = $discount['ACTIVE_TO']; // объект Bitrix\Main\Type\DateTime
}
?>
Дальше выводим $saleDateTo в нужном месте шаблона. Можно отформатировать через format():
<?=$saleDateTo->format('d.m.Y')?>
Выводим количество дней до конца скидки
Ещё актуальнее - показать не просто дату, а сколько дней осталось. Если осталось меньше одного дня - пишем "закончится сегодня".
Используем D7-шный DateTime, чтобы нормально посчитать разницу:
<?
use Bitrix\Main\Type\DateTime;
$now = new DateTime();
if ($saleDateTo instanceof DateTime) {
$diff = $now->getDiff($saleDateTo);
$daysLeft = (int)$diff->days;
if ($daysLeft > 1) {
$daysToEndSale = 'Закончится через ' . $daysLeft . ' дн.';
} elseif ($daysLeft == 1) {
$daysToEndSale = 'Закончится завтра';
} elseif ($daysLeft == 0) {
$daysToEndSale = 'Закончится сегодня';
} else {
$daysToEndSale = '';
}
}
?>
Здесь getDiff() возвращает объект DateInterval, у которого есть свойство days - количество полных дней между датами. Надёжнее, чем делить секунды на 86400, потому что учитывает перевод часов и летнее время.
В переменной $daysToEndSale - готовый текст. Выводим и оформляем где нужно:
<?if ($daysToEndSale):?>
<div class="discount-timer">
<?=$daysToEndSale?>
</div>
<?endif;?>
Кеширование (чтобы не дёргать базу на каждом хите)
Запрос к DiscountTable на каждом показе товара - лишняя нагрузка. Битрикс умеет кешировать через CacheManager:
<?
use Bitrix\Main\Data\Cache;
use Bitrix\Catalog\DiscountTable;
$cache = Cache::createInstance();
$cacheKey = 'discount_date_' . $PRODUCT_ID;
$cacheDir = '/discounts';
if ($cache->initCache(3600, $cacheKey, $cacheDir)) {
$saleDateTo = $cache->getVars()['saleDateTo'];
} elseif ($cache->startDataCache()) {
$discountIterator = DiscountTable::getList([
'filter' => [
'=ACTIVE' => 'Y',
'=%SITE_ID' => SITE_ID,
['=PRODUCT_ID' => $PRODUCT_ID],
],
'select' => ['ACTIVE_TO'],
'order' => ['SORT' => 'ASC'],
'limit' => 1,
]);
$saleDateTo = null;
if ($discount = $discountIterator->fetch()) {
$saleDateTo = $discount['ACTIVE_TO'];
}
$cache->endDataCache(['saleDateTo' => $saleDateTo]);
}
?>
Если скидка привязана не к товару, а к цене
Бывает, что скидка висит на типе цены (например, "Оптовая цена - 20%") и не привязана к конкретному товару через PRODUCT_ID. В этом случае искать через DiscountTable бесполезно - нужно лезть в \Bitrix\Catalog\PriceTable и проверять PRICE против BASE_PRICE:
<?
use Bitrix\Catalog\PriceTable;
use Bitrix\Catalog\GroupTable;
$baseGroupId = GroupTable::getBasePriceGroup(); // ID базового типа цены
$priceIterator = PriceTable::getList([
'filter' => [
'=PRODUCT_ID' => $PRODUCT_ID,
'!=CATALOG_GROUP_ID' => $baseGroupId,
],
'select' => [
'PRICE', 'CURRENCY',
'CATALOG_GROUP_ID',
'CATALOG_GROUP.NAME',
],
]);
while ($price = $priceIterator->fetch()) {
// Тут уже другая история - проценты и наценки
// Можно вывести: "Цена по акции: X руб."
}
?>
Красивый обратный отсчёт (опционально)
Если хочется не просто текст, а живой таймер - передаём Unix-штамп в JavaScript и крутим на клиенте:
<?if ($saleDateTo instanceof DateTime):?>
<div class="discount-timer"
data-until="<?=$saleDateTo->getTimestamp()?>"
data-label="до конца скидки"></div>
<?endif;?>
А на JS навешиваете любой счётчик - это уже дело вкуса и фронта.
Итог
Старый CCatalogDiscount::GetList работает, но D7-версия удобнее:
- даты сразу приходят объектами
DateTime, не надо парсить строки - фильтры читаемые, а не магические массивы с плюсами
- результат можно кешировать штатными средствами
- не нужен
strtotimeи деление на 86400 - всё считаетgetDiff()