Разработка мегаменю с баннерами и акциями 1С-Битрикс
Мегаменю — не только навигация. В крупных магазинах правая колонка дропдауна отдаётся под маркетинг: баннер текущей акции в категории, список товаров со скидкой, «новинки раздела». Это увеличивает видимость промо без отдельного рекламного места на странице.
Управление баннерами мегаменю
Баннеры привязываются к разделам каталога через HL-блок MegaMenuBanner:
b_uts_megamenu_banner
├── UF_SECTION_ID — раздел каталога (b_iblock_section.ID)
├── UF_IMAGE — изображение баннера (b_file.ID)
├── UF_TITLE — заголовок (опционально)
├── UF_SUBTITLE — подзаголовок
├── UF_LINK — URL клика
├── UF_ACTIVE_FROM — начало показа
├── UF_ACTIVE_TO — конец показа
├── UF_SORT — порядок сортировки
└── UF_ACTIVE — активность
Менеджер может менять баннеры из административной части HL-блока без привлечения разработчика.
Загрузка баннеров при построении меню
namespace Local\Menu;
use Bitrix\Highloadblock\HighloadBlockTable;
use Bitrix\Main\Type\DateTime;
class MegaMenuBannerRepository
{
public static function getForSections(array $sectionIds): array
{
if (empty($sectionIds)) return [];
$hlBlock = HighloadBlockTable::getById(MEGAMENU_BANNER_HLBLOCK_ID)->fetch();
$entity = HighloadBlockTable::compileEntity($hlBlock);
$dataClass = $entity->getDataClass();
$now = new DateTime();
$res = $dataClass::getList([
'filter' => [
'UF_SECTION_ID' => $sectionIds,
'UF_ACTIVE' => true,
[
'LOGIC' => 'OR',
['<=UF_ACTIVE_FROM' => $now, '>=UF_ACTIVE_TO' => $now],
['UF_ACTIVE_FROM' => false],
],
],
'order' => ['UF_SECTION_ID' => 'ASC', 'UF_SORT' => 'ASC'],
'select' => ['UF_SECTION_ID', 'UF_IMAGE', 'UF_TITLE', 'UF_SUBTITLE', 'UF_LINK'],
]);
$result = [];
while ($row = $res->fetch()) {
$sectionId = $row['UF_SECTION_ID'];
if (!isset($result[$sectionId])) {
$result[$sectionId] = [];
}
$row['UF_IMAGE_SRC'] = \CFile::ResizeImageGet(
$row['UF_IMAGE'],
['width' => 280, 'height' => 180],
BX_RESIZE_IMAGE_EXACT
)['src'] ?? null;
$result[$sectionId][] = $row;
}
return $result;
}
}
Актуальные акции в мегаменю
Параллельно с баннерами — список товаров раздела, участвующих в акциях. Скидочные товары определяются через b_catalog_price:
public static function getDiscountedProducts(int $sectionId, int $limit = 4): array
{
$conn = \Bitrix\Main\Application::getConnection();
$result = $conn->query("
SELECT
be.ID,
be.NAME,
be.DETAIL_PAGE_URL,
be.PREVIEW_PICTURE,
base_p.PRICE AS price,
sale_p.PRICE AS sale_price
FROM b_iblock_element be
JOIN b_iblock_section_element bse ON bse.IBLOCK_ELEMENT_ID = be.ID
JOIN b_catalog_price base_p
ON base_p.PRODUCT_ID = be.ID
AND base_p.CATALOG_GROUP_ID = 1
JOIN b_catalog_price sale_p
ON sale_p.PRODUCT_ID = be.ID
AND sale_p.CATALOG_GROUP_ID = 2 -- группа «Акционная цена»
WHERE bse.IBLOCK_SECTION_ID = {$sectionId}
AND be.IBLOCK_ID = " . CATALOG_IBLOCK_ID . "
AND be.ACTIVE = 'Y'
AND sale_p.PRICE < base_p.PRICE
ORDER BY (base_p.PRICE - sale_p.PRICE) / base_p.PRICE DESC
LIMIT {$limit}
");
$products = [];
while ($row = $result->fetch()) {
$row['DISCOUNT_PCT'] = round(
($row['price'] - $row['sale_price']) / $row['price'] * 100
);
$row['IMAGE_SRC'] = $row['PREVIEW_PICTURE']
? \CFile::ResizeImageGet($row['PREVIEW_PICTURE'], ['width' => 100, 'height' => 100], BX_RESIZE_IMAGE_PROPORTIONAL)['src']
: null;
$products[] = $row;
}
return $products;
}
Шаблон мегаменю с баннером и акциями
<div class="megamenu__dropdown">
<div class="megamenu__inner megamenu__inner--with-promo">
<!-- Навигация по подкатегориям -->
<div class="megamenu__nav">
<?php foreach ($category['children'] as $sub): ?>
<a href="<?= htmlspecialchars($sub['SECTION_PAGE_URL']) ?>"
class="megamenu__subcat">
<?= htmlspecialchars($sub['NAME']) ?>
</a>
<?php endforeach ?>
</div>
<!-- Акционные товары -->
<?php if (!empty($discounted[$category['ID']])): ?>
<div class="megamenu__sales">
<div class="megamenu__section-title">Скидки в разделе</div>
<?php foreach ($discounted[$category['ID']] as $product): ?>
<a href="<?= htmlspecialchars($product['DETAIL_PAGE_URL']) ?>"
class="megamenu__sale-item">
<?php if ($product['IMAGE_SRC']): ?>
<img src="<?= $product['IMAGE_SRC'] ?>" alt="" width="56" height="56">
<?php endif ?>
<div>
<div class="megamenu__sale-name"><?= htmlspecialchars($product['NAME']) ?></div>
<div class="megamenu__sale-prices">
<span class="old"><?= number_format($product['price'], 0, '', ' ') ?> ₽</span>
<span class="new"><?= number_format($product['sale_price'], 0, '', ' ') ?> ₽</span>
<span class="badge">-<?= $product['DISCOUNT_PCT'] ?>%</span>
</div>
</div>
</a>
<?php endforeach ?>
</div>
<?php endif ?>
<!-- Баннер -->
<?php $banners = $menuBanners[$category['ID']] ?? [] ?>
<?php if (!empty($banners[0]) && $banners[0]['UF_IMAGE_SRC']): ?>
<div class="megamenu__banner">
<a href="<?= htmlspecialchars($banners[0]['UF_LINK']) ?>">
<img src="<?= $banners[0]['UF_IMAGE_SRC'] ?>"
alt="<?= htmlspecialchars($banners[0]['UF_TITLE'] ?? '') ?>"
width="280" height="180" loading="lazy">
<?php if ($banners[0]['UF_TITLE']): ?>
<div class="megamenu__banner-title">
<?= htmlspecialchars($banners[0]['UF_TITLE']) ?>
</div>
<?php endif ?>
</a>
</div>
<?php endif ?>
</div>
</div>
CSS grid для трёхколоночного layout
.megamenu__inner--with-promo {
display: grid;
grid-template-columns: 220px 1fr 300px;
min-height: 320px;
}
.megamenu__sales {
padding: 1rem 1.5rem;
border-right: 1px solid #eee;
}
.megamenu__sale-item {
display: flex;
gap: 0.75rem;
padding: 0.625rem 0;
border-bottom: 1px solid #f0f0f0;
text-decoration: none;
color: inherit;
transition: background 0.1s;
}
.megamenu__sale-prices .old {
text-decoration: line-through;
color: #999;
font-size: 0.75rem;
}
.megamenu__sale-prices .new {
font-weight: 600;
color: var(--color-sale);
}
.megamenu__banner {
position: relative;
overflow: hidden;
}
.megamenu__banner img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.megamenu__banner-title {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 1rem;
background: linear-gradient(transparent, rgba(0,0,0,.6));
color: #fff;
font-weight: 600;
}
Кеширование и обновление при смене акций
Баннеры и акции кешируются на 30 минут (акции меняются чаще, чем структура разделов). Инвалидация — при сохранении HL-блока баннеров:
AddEventHandler('main', 'OnAfterHLBlockElementAdd', '\Local\Menu\MegaMenuCache::clear');
AddEventHandler('main', 'OnAfterHLBlockElementUpdate', '\Local\Menu\MegaMenuCache::clear');
Сроки реализации
| Конфигурация | Срок |
|---|---|
| Баннеры через HL-блок + вывод в мегаменю | 3–4 дня |
| + акционные товары из b_catalog_price | +2 дня |
| + ротация баннеров, ограничение по датам | +1–2 дня |







