Реализация конфигуратора продукта на сайте
Конфигуратор продукта — это интерфейс, в котором покупатель собирает финальный вариант товара из набора параметров: материал, размер, цвет, комплектация, гравировка. В отличие от простого выбора варианта (S/M/L), конфигуратор работает с комбинаторикой: число вариантов может достигать миллионов, и хранить каждый как отдельный SKU невозможно. Итоговая цена и состав вычисляются динамически.
Архитектурный выбор: SKU vs динамическая конфигурация
Табличные варианты (SKU) подходят, когда комбинаций до нескольких тысяч, и каждая имеет свой остаток на складе. Стандартный подход для одежды.
Динамическая конфигурация нужна, когда:
- Количество возможных комбинаций > 10 000
- Каждый параметр независимо влияет на цену
- Производство под заказ (нет готового склада)
- Нужна визуализация результата в реальном времени
Схема данных конфигуратора
CREATE TABLE configurator_products (
id BIGSERIAL PRIMARY KEY,
product_id BIGINT NOT NULL REFERENCES products(id),
name VARCHAR(255) NOT NULL,
base_price NUMERIC(12,2) NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true
);
CREATE TABLE configurator_option_groups (
id BIGSERIAL PRIMARY KEY,
configurator_id BIGINT NOT NULL REFERENCES configurator_products(id),
name VARCHAR(255) NOT NULL, -- "Материал", "Цвет", "Гравировка"
type VARCHAR(50) NOT NULL,
-- select | radio | checkbox | text | number | color
is_required BOOLEAN NOT NULL DEFAULT true,
sort_order INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE configurator_options (
id BIGSERIAL PRIMARY KEY,
group_id BIGINT NOT NULL REFERENCES configurator_option_groups(id),
label VARCHAR(255) NOT NULL,
value VARCHAR(255) NOT NULL,
price_modifier NUMERIC(12,2) NOT NULL DEFAULT 0, -- абсолютная надбавка
price_modifier_pct NUMERIC(5,2) NOT NULL DEFAULT 0, -- процентная надбавка
modifier_type VARCHAR(20) NOT NULL DEFAULT 'add', -- add | multiply | fixed
sku_suffix VARCHAR(50), -- для формирования SKU
is_available BOOLEAN NOT NULL DEFAULT true,
image_url VARCHAR(500), -- превью опции
sort_order INTEGER NOT NULL DEFAULT 0
);
-- Правила зависимостей между опциями
CREATE TABLE configurator_rules (
id BIGSERIAL PRIMARY KEY,
configurator_id BIGINT NOT NULL REFERENCES configurator_products(id),
rule_type VARCHAR(50) NOT NULL, -- requires | excludes | enables
if_option_id BIGINT NOT NULL REFERENCES configurator_options(id),
then_option_id BIGINT NOT NULL REFERENCES configurator_options(id)
);
Вычисление цены
Цена конфигурации вычисляется сервером, не клиентом:
class ConfiguratorPriceCalculator
{
public function calculate(
ConfiguratorProduct $configurator,
array $selectedOptions // [group_id => option_id | value]
): PriceBreakdown
{
$base = $configurator->base_price;
$breakdown = [['label' => 'Базовая цена', 'amount' => $base]];
$total = $base;
foreach ($selectedOptions as $groupId => $selection) {
$group = $configurator->optionGroups->find($groupId);
if ($group->type === 'text' || $group->type === 'number') {
// Для свободного ввода — фиксированная надбавка за группу
$modifier = $group->text_price_modifier ?? 0;
} else {
$option = ConfiguratorOption::findOrFail($selection);
$modifier = $this->resolveModifier($option, $total);
}
if ($modifier != 0) {
$breakdown[] = [
'label' => $group->name . ($group->type !== 'text' ? ': ' . $option->label : ''),
'amount' => $modifier,
];
$total += $modifier;
}
}
return new PriceBreakdown($total, $breakdown);
}
private function resolveModifier(ConfiguratorOption $option, float $currentTotal): float
{
return match($option->modifier_type) {
'add' => $option->price_modifier,
'multiply' => $currentTotal * ($option->price_modifier_pct / 100),
'fixed' => $option->price_modifier - $currentTotal,
};
}
}
Клиент показывает цену предварительно (из JS-расчёта), но при добавлении в корзину цена всегда пересчитывается сервером. Доверять клиентскому расчёту в финансовом контексте нельзя.
Правила зависимостей
Некоторые опции взаимно исключают друг друга или требуют выбора другой опции. Правила проверяются и на клиенте (UX), и на сервере (валидация):
class ConfiguratorRuleEngine
{
public function validate(ConfiguratorProduct $configurator, array $selected): ValidationResult
{
$errors = [];
foreach ($configurator->rules as $rule) {
$ifSelected = in_array($rule->if_option_id, $selected);
$thenSelected = in_array($rule->then_option_id, $selected);
match($rule->rule_type) {
'requires' => $ifSelected && !$thenSelected
? $errors[] = "Опция требует выбора: {$rule->thenOption->label}"
: null,
'excludes' => $ifSelected && $thenSelected
? $errors[] = "Несовместимые опции: {$rule->thenOption->label}"
: null,
'enables' => !$ifSelected
? $this->disableOption($rule->then_option_id)
: null,
};
}
return new ValidationResult(empty($errors), $errors);
}
}
Формирование SKU конфигурации
Для производства важно иметь детерминированный код конфигурации:
class ConfigurationSkuBuilder
{
public function build(ConfiguratorProduct $configurator, array $selected): string
{
$parts = [$configurator->product->sku];
$sortedOptions = collect($selected)
->sortKeys()
->map(fn($optionId, $groupId) =>
ConfiguratorOption::find($optionId)?->sku_suffix ?? $optionId
);
return implode('-', array_merge($parts, $sortedOptions->all()));
// Пример: CHAIR-BLK-LEATH-ARMB-CUST
}
}
API для фронтенда
Конфигуратор работает через AJAX. Минимальный набор эндпоинтов:
GET /api/configurators/{id} — полная схема конфигуратора
POST /api/configurators/{id}/price — пересчёт цены по выбору
POST /api/configurators/{id}/validate — валидация набора опций
POST /api/cart/add-configuration — добавление конфигурации в корзину
Запрос пересчёта цены:
POST /api/configurators/5/price
{
"selected": {
"1": 12,
"2": 7,
"3": "Иван Иванов"
}
}
Ответ:
{
"total": 4850.00,
"breakdown": [
{ "label": "Базовая цена", "amount": 3500.00 },
{ "label": "Материал: Кожа натуральная", "amount": 1200.00 },
{ "label": "Цвет: Чёрный", "amount": 0 },
{ "label": "Гравировка", "amount": 150.00 }
]
}
Сохранение конфигурации в корзине
Конфигурация сохраняется как JSON в позиции корзины:
ALTER TABLE cart_items ADD COLUMN configuration JSONB;
ALTER TABLE order_items ADD COLUMN configuration JSONB;
ALTER TABLE order_items ADD COLUMN configuration_sku VARCHAR(255);
{
"configurator_id": 5,
"groups": {
"1": { "option_id": 12, "label": "Кожа натуральная" },
"2": { "option_id": 7, "label": "Чёрный" },
"3": { "type": "text", "value": "Иван Иванов" }
},
"sku": "CHAIR-LEATH-BLK-CUST",
"price_at_add": 4850.00
}
При добавлении конфигурации в корзину цена пересчитывается сервером и фиксируется. Изменение прайса не влияет на уже добавленные конфигурации.
Административный интерфейс
Менеджер должен иметь возможность:
- Создавать и редактировать конфигураторы без программиста
- Управлять порядком групп и опций
- Задавать правила зависимостей через UI (не SQL)
- Предпросматривать конфигуратор до публикации
Сроки реализации
- Базовая схема + API пересчёта цены + добавление в корзину: 5–7 дней
- Правила зависимостей (requires/excludes): 2–3 дня
- Административный интерфейс для управления конфигуратором: 3–4 дня
- Визуальный превью (смена изображения при выборе опции): 2–3 дня
- Интеграция с системой производственных заданий: +3–5 дней
Типовой проект: 2–4 недели в зависимости от сложности правил и требований к визуализации.







