Разработка формы подбора товара 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Разработка формы подбора товара 1С-Битрикс
Средняя
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    747
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Разработка формы подбора товара 1С-Битрикс

Форма подбора товара — интерактивный помощник, который задаёт пользователю вопросы о его потребностях и на выходе предлагает конкретные товары из каталога. Применяется там, где ассортимент велик или товары технически сложны: выбор ноутбука, шин, краски, промышленного оборудования. По сути это рекомендательная система, замаскированная под опросник. Ключевая техническая задача — алгоритм сопоставления ответов с товарами в инфоблоке.

Два подхода к подбору

1. Прямая фильтрация. Каждый ответ пользователя = фильтр по свойству инфоблока. Итог = пересечение всех фильтров. Просто, предсказуемо, но плохо работает когда пользователь дал «неточные» ответы — результат может быть пустым.

2. Scoring (балльная система). Каждый вариант ответа добавляет баллы определённым товарам или категориям. Товары ранжируются по сумме баллов. Лучше для рекомендаций, сложнее в настройке.

Для большинства каталогов — комбинация: обязательные фильтры (например, бюджет) + скоринг по дополнительным критериям.

Структура данных: маппинг вопросов на свойства

// Конфигурация подборщика (хранится в b_option или HL-блоке)
$selectorConfig = [
    'iblock_id' => CATALOG_IBLOCK_ID,
    'questions' => [
        [
            'id'     => 'q_budget',
            'text'   => 'Ваш бюджет',
            'type'   => 'select',
            'filter_mode' => 'range',  // Тип фильтрации
            'options' => [
                ['value' => 'low',    'label' => 'до 30 000 ₽',  'filter' => ['<CATALOG_PRICE_1' => 30000]],
                ['value' => 'mid',    'label' => '30–60 000 ₽',  'filter' => ['>=CATALOG_PRICE_1' => 30000, '<CATALOG_PRICE_1' => 60000]],
                ['value' => 'high',   'label' => 'от 60 000 ₽',  'filter' => ['>=CATALOG_PRICE_1' => 60000]],
            ],
        ],
        [
            'id'     => 'q_purpose',
            'text'   => 'Для каких задач',
            'type'   => 'radio',
            'filter_mode' => 'property',
            'property_code' => 'PURPOSE',  // Код свойства в инфоблоке
            'options' => [
                ['value' => 'work',   'label' => 'Работа / офис',   'property_value' => 'WORK'],
                ['value' => 'gaming', 'label' => 'Игры',            'property_value' => 'GAMING'],
                ['value' => 'study',  'label' => 'Учёба',           'property_value' => 'STUDY'],
            ],
        ],
        [
            'id'     => 'q_weight',
            'text'   => 'Важен ли вес устройства?',
            'type'   => 'radio',
            'filter_mode' => 'scoring',
            'options' => [
                ['value' => 'yes', 'label' => 'Да, беру с собой',  'scores' => ['lightweight' => 10]],
                ['value' => 'no',  'label' => 'Нет, стационарный', 'scores' => ['performance' => 5]],
            ],
        ],
    ],
];

Алгоритм фильтрации и скоринга

class ProductSelector
{
    private array $config;

    public function findProducts(array $answers): array
    {
        $hardFilters  = ['IBLOCK_ID' => $this->config['iblock_id'], 'ACTIVE' => 'Y'];
        $scoringTags  = []; // tag => score

        foreach ($this->config['questions'] as $question) {
            $answer = $answers[$question['id']] ?? null;
            if ($answer === null) continue;

            $selectedOption = null;
            foreach ($question['options'] as $opt) {
                if ($opt['value'] === $answer) {
                    $selectedOption = $opt;
                    break;
                }
            }

            if (!$selectedOption) continue;

            switch ($question['filter_mode']) {
                case 'range':
                case 'property':
                    // Добавить в жёсткие фильтры
                    $hardFilters = array_merge($hardFilters, $selectedOption['filter'] ?? []);
                    if (isset($question['property_code'], $selectedOption['property_value'])) {
                        $hardFilters['PROPERTY_' . $question['property_code']] = $selectedOption['property_value'];
                    }
                    break;

                case 'scoring':
                    foreach ($selectedOption['scores'] ?? [] as $tag => $score) {
                        $scoringTags[$tag] = ($scoringTags[$tag] ?? 0) + $score;
                    }
                    break;
            }
        }

        // Получить товары по жёстким фильтрам
        $result = \CIBlockElement::GetList(
            ['SORT' => 'ASC'],
            $hardFilters,
            false,
            ['nPageSize' => 20],
            ['ID', 'NAME', 'DETAIL_PAGE_URL', 'PREVIEW_PICTURE', 'CATALOG_PRICE_1', 'PROPERTY_TAGS']
        );

        $products = [];
        while ($item = $result->GetNext()) {
            $score = 0;
            // Применить скоринг по тегам товара
            $productTags = explode(',', $item['PROPERTY_TAGS_VALUE'] ?? '');
            foreach ($scoringTags as $tag => $tagScore) {
                if (in_array(trim($tag), array_map('trim', $productTags))) {
                    $score += $tagScore;
                }
            }
            $item['_SCORE'] = $score;
            $products[]     = $item;
        }

        // Сортировать по скорингу (убывание)
        usort($products, fn($a, $b) => $b['_SCORE'] <=> $a['_SCORE']);

        return $products;
    }
}

Клиентская часть

class ProductSelectorUI {
    constructor(configJson) {
        this.config  = configJson;
        this.answers = {};
        this.step    = 0;
        this.renderStep();
    }

    renderStep() {
        const question = this.config.questions[this.step];
        const container = document.getElementById('selector-step');

        container.innerHTML = `
            <h3>${question.text}</h3>
            <div class="selector-options">
                ${question.options.map(opt => `
                    <button class="selector-option" data-value="${opt.value}" data-question="${question.id}">
                        ${opt.label}
                    </button>
                `).join('')}
            </div>
        `;

        container.querySelectorAll('.selector-option').forEach(btn => {
            btn.addEventListener('click', e => {
                const questionId = e.target.dataset.question;
                const value      = e.target.dataset.value;
                this.selectAnswer(questionId, value);
            });
        });

        this.updateProgress();
    }

    selectAnswer(questionId, value) {
        this.answers[questionId] = value;

        if (this.step < this.config.questions.length - 1) {
            this.step++;
            this.renderStep();
        } else {
            this.fetchResults();
        }
    }

    async fetchResults() {
        document.getElementById('selector-step').innerHTML = '<div class="loading">Подбираем для вас...</div>';

        const response = await fetch('/local/ajax/product_selector.php', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({answers: this.answers, sessid: BX.bitrix_sessid()}),
        });

        const data = await response.json();
        this.renderResults(data.products);
    }

    renderResults(products) {
        const container = document.getElementById('selector-results');
        if (products.length === 0) {
            container.innerHTML = '<p>К сожалению, по вашим критериям ничего не найдено. <a href="/catalog/">Смотреть весь каталог</a></p>';
            return;
        }

        container.innerHTML = products.slice(0, 6).map(p => `
            <div class="product-card">
                <img src="${p.PREVIEW_PICTURE?.SRC || ''}" alt="${p.NAME}">
                <h4><a href="${p.DETAIL_PAGE_URL}">${p.NAME}</a></h4>
                <div class="price">${p.CATALOG_PRICE_1} ₽</div>
                <a href="${p.DETAIL_PAGE_URL}" class="btn">Подробнее</a>
            </div>
        `).join('');

        document.getElementById('selector-container').style.display = 'none';
        document.getElementById('selector-results-container').style.display = 'block';
    }

    updateProgress() {
        const bar = document.getElementById('selector-progress');
        if (bar) bar.style.width = ((this.step / this.config.questions.length) * 100) + '%';
    }
}

Результат и лид

Если подходящих товаров нет — форма предлагает оставить контакт для консультации:

if (empty($products)) {
    echo json_encode([
        'products' => [],
        'show_contact_form' => true,
        'message' => 'Мы подберём для вас индивидуально',
    ]);
    exit;
}

Если пользователь оставляет контакт после подбора — сохраняем выбранные параметры в комментарии лида (маркетинговая ценность: менеджер сразу знает, что ищет клиент).

Сроки разработки

Вариант Состав Срок
Прямая фильтрация Вопросы → фильтры → список товаров 4–6 дней
Со скорингом + Балльная система, ранжирование 6–10 дней
С конфигуратором Управление вопросами через административную панель 10–15 дней