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

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка форми підбору товару 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • 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
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка форми підбору товару 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 днів