Проект: lider-drupal
Дата: 2026-02-12
Проблема: Как определить совместимость деталей между моделями
У нас есть три источника данных, которые нужно связать:
┌──────────────────────────────────────────────────────────────┐
│ 1. BAZON CSV │
│ Модель: "4-FH", "4-FM", "6-R" │
│ БМ_0: "4-FM" (дополнительная модель) │
│ БМ_1: "6-R" (ещё одна) │
│ │
│ 2. РЕАЛЬНЫЕ МОДЕЛИ (NocoDB) │
│ Volvo FH4 2012, Volvo FM4 2012 │
│ Scania R-6 2010, Scania G-6 2010 │
│ │
│ 3. КАК ИЩУТ ЛЮДИ (поведение) │
│ "запчасти volvo фш4" │
│ "фара вольво 4 фш" │
│ "volvo fh 2012" │
└──────────────────────────────────────────────────────────────┘
Вопрос: Как связать эти три источника и определить совместимость?
┌─────────────────────────────────────────────────────────────┐
│ Таблица: Модели │
├─────────────────────────────────────────────────────────────┤
│ │
│ id | name | old_code | generation | platform │
│────┼───────────────┼──────────┼────────────┼───────────────┤
│ 1 | Volvo FH4 | 4-FH | v4 | euro6 │
│ 2 | Volvo FM4 | 4-FM | v4 | euro6 │
│ 3 | Volvo FH3 | 3-FH | v3 | euro5 │
│ 4 | Volvo FM3 | 3-FM | v3 | euro5 │
│────┼───────────────┼──────────┼────────────┼───────────────┤
│ 10 | Scania R-6 | 6-R | s6 | pgr │
│ 11 | Scania G-6 | 6-G | s6 | pgr │
│ 12 | Scania P-6 | 6-P | s6 | pgr │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Таблица: Узлы │
├─────────────────────────────────────────────────────────────┤
│ │
│ id | name | code | aggregate_id | compat_type │
│────┼───────────┼───────────┼──────────────┼────────────────┤
│ 1 | Оптика | optics | 1 (Кабина) | model │
│ 2 | Подвеска | suspension| 2 (Шасси) | platform │
│ 3 | Турбина | turbo | 3 (Силовой) | engine │
└─────────────────────────────────────────────────────────────┘
Импорт из NocoDB → Drupal:
// Скрипт импорта справочников
// drush scr import-models.php
use Drupal\taxonomy\Entity\Term;
// 1. Импорт моделей из NocoDB
$nocodb_models = getNoCDBModels(); // API call
foreach ($nocodb_models as $row) {
$term = Term::create([
'vid' => 'models',
'name' => $row['name'], // "Volvo FH4"
'field_old_code' => $row['old_code'], // "4-FH"
'field_generation' => $row['generation'], // "v4"
'field_platform' => $row['platform'], // "euro6"
'field_nocodb_id' => $row['id'], // 1
]);
$term->save();
}
Результат в Drupal:
taxonomy/models:
- tid:100, name:"Volvo FH4", old_code:"4-FH", platform:"euro6"
- tid:101, name:"Volvo FM4", old_code:"4-FM", platform:"euro6"
- tid:102, name:"Scania R-6", old_code:"6-R", platform:"pgr"
Импорт CSV → Drupal с определением совместимости:
// ЭТАП 1: Парсинг CSV и определение моделей
function parseCompatibility($csv_row) {
// Из CSV:
// Модель: "4-FH"
// БМ_0: "4-FM"
// БМ_1: "6-R"
$old_codes = [
$csv_row['Модель'], // "4-FH"
$csv_row['БМ_0'], // "4-FM"
$csv_row['БМ_1'], // "6-R"
];
// Убрать пустые
$old_codes = array_filter($old_codes);
// Найти taxonomy terms по old_code
$model_terms = [];
foreach ($old_codes as $code) {
$terms = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties([
'vid' => 'models',
'field_old_code' => $code,
]);
if (!empty($terms)) {
$model_terms[] = reset($terms);
}
}
return $model_terms;
// → [tid:100 (Volvo FH4), tid:101 (Volvo FM4), tid:102 (Scania R-6)]
}
Создание Product:
function createProduct($csv_row) {
$oem = $csv_row['OEM'];
// 1. Определить совместимые модели
$models = parseCompatibility($csv_row);
// 2. Определить узел
$node = determineNode($csv_row['Наименование полное']);
// 3. Узел → агрегат → тип совместимости
$aggregate = $node->field_aggregate->entity;
$compat_type = $aggregate->field_compatibility_type->value;
// 4. Расширить совместимость по типу
$models = expandCompatibility($models, $compat_type);
// 5. Создать Product
$product = Product::create([
'title' => generateTitle($csv_row),
'field_oem' => $oem,
'field_models' => $models, // ← здесь все совместимые модели
'field_node' => $node,
'field_aggregate' => $aggregate,
]);
$product->save();
}
/**
* Расширяет список моделей на основе типа совместимости
*/
function expandCompatibility($base_models, $compat_type) {
$result = [];
switch ($compat_type) {
case 'model':
// MODEL: только указанные модели
// Фара FH4 НЕ подходит к FM4
return $base_models;
case 'platform':
// PLATFORM: все модели той же платформы
// Амортизатор FH4 = FM4 (euro6)
foreach ($base_models as $model) {
$platform = $model->field_platform->value;
// Найти все модели с той же платформой
$siblings = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties([
'vid' => 'models',
'field_platform' => $platform,
]);
$result = array_merge($result, $siblings);
}
break;
case 'engine':
// ENGINE: все модели с тем же двигателем
// Турбина D13K → все модели с D13K
foreach ($base_models as $model) {
$engine = extractEngine($model); // "D13K"
$models_with_engine = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties([
'vid' => 'models',
'field_engines' => $engine, // JSON field
]);
$result = array_merge($result, $models_with_engine);
}
break;
case 'generation':
// GENERATION: все модели того же поколения
// Блок ECU FH4 = FM4 (v4)
foreach ($base_models as $model) {
$generation = $model->field_generation->entity;
$models_in_gen = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties([
'vid' => 'models',
'field_generation' => $generation->id(),
]);
$result = array_merge($result, $models_in_gen);
}
break;
case 'universal':
// UNIVERSAL: широкая совместимость
// Масляный фильтр → все модели марки
foreach ($base_models as $model) {
$brand = $model->field_generation->entity->field_brand->entity;
$all_brand_models = getBrandModels($brand);
$result = array_merge($result, $all_brand_models);
}
break;
}
// Убрать дубли
return array_unique($result, SORT_REGULAR);
}
CSV:
Модель: 4-FH
БМ_0: 4-FM
Наименование: Фара правая
Шаги:
Парсинг:
- 4-FH → tid:100 (Volvo FH4)
- 4-FM → tid:101 (Volvo FM4)
Определение узла:
- "Фара правая" → Оптика → Агрегат "Кабина" → compat_type = model
Расширение:
- Тип model → НЕ расширяем
- Результат: [FH4, FM4] — только указанные
Product:
yaml
field_models: [tid:100, tid:101]
field_compatibility: "Volvo FH4, FM4 (2012+)"
Почему FM4? Хотя фары РАЗНЫЕ, в BAZON их указали как совместимые (БМ_0). Возможно, физически подходят.
CSV:
Модель: 4-FH
БМ_0: (пусто)
Наименование: Амортизатор передний
Шаги:
Парсинг:
- 4-FH → tid:100 (Volvo FH4)
Узел:
- "Амортизатор" → Подвеска → Агрегат "Шасси" → compat_type = platform
Расширение (КЛЮЧЕВОЕ):
```php
platform = "euro6"
SELECT * FROM models WHERE platform = "euro6"
→ Volvo FH4, Volvo FM4
```
yaml
field_models: [tid:100, tid:101] ← автоматически добавлен FM4
field_compatibility: "Volvo FH4, FM4 (euro6)"Результат: Даже если в CSV только FH4, система АВТОМАТИЧЕСКИ добавит FM4 (та же платформа).
CSV:
Модель: 4-FH
Наименование: Турбина D13K
Шаги:
Парсинг:
- 4-FH → tid:100 (Volvo FH4)
Узел:
- "Турбина D13K" → Турбонаддув → Агрегат "Силовой" → compat_type = engine
Извлечение двигателя:
php
extractEngine("Турбина D13K") → "D13K"
Расширение:
php
SELECT * FROM models WHERE engines CONTAINS "D13K"
→ Volvo FH4 (D13K), FM4 (D13K), FH3 (D13K)
Product:
yaml
field_models: [tid:100, tid:101, tid:102]
field_engine_type: "D13K"
field_compatibility: "Volvo двигатель D13K (все модели)"
/zapchasti/
/volvo/
/fh4/
/optics/ ← Фильтр: model=FH4 AND node=optics
/suspension/ ← Фильтр: model=FH4 AND node=suspension
/engine/
/fm4/
/optics/
/suspension/
/scania/
/r-6/
/optics/
Проблема: Дублирование товаров.
- Амортизатор показан и в /volvo/fh4/suspension/, и в /volvo/fm4/suspension/
Решение: Canonical на Product.
- Canonical: /zapchasti/amortizator-20499340/
- Категория FH4: index, follow
- Категория FM4: index, follow
- Оба ведут на один товар
/zapchasti/
/{brand}/{model}/ ← Все запчасти модели
/{brand}/{model}/{node}/ ← Фильтр по узлу
/{node}/ ← Все детали узла (все марки)
/{aggregate}/ ← Все детали агрегата
Views для каждой страницы:
# /volvo/fh4/
path: /zapchasti/{brand}/{model}
filter: field_models CONTAINS {model_tid}
display: grid, 20 items/page
# /volvo/fh4/optics/
path: /zapchasti/{brand}/{model}/{node}
filter:
- field_models CONTAINS {model_tid}
- field_node = {node_tid}
# /optics/ (все марки)
path: /zapchasti/{node}
filter: field_node = {node_tid}
Преимущества:
- ✅ Нет дублей контента (один товар = одна страница)
- ✅ Множественные пути навигации
- ✅ Простая фильтрация (Facets)
Основная навигация:
Главная → Volvo → FH4 → [список всех деталей]
↓
Фильтры (боковая панель):
- Кабина и кузов (12)
├─ Оптика (4)
├─ Зеркала (2)
- Шасси (8)
├─ Подвеска (3)
URL:
/zapchasti/volvo/fh4/ ← Все детали FH4
/zapchasti/volvo/fh4/?node=optics ← Фильтр Оптика
/zapchasti/volvo/fh4/?aggregate=cabin ← Фильтр Кабина
Facets навигация:
┌────────────────────────────────────────────────────────────┐
│ Запчасти Volvo FH4 2012 │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌──────────────────────────────────┐ │
│ │ ФИЛЬТРЫ │ │ ТОВАРЫ (42) │ │
│ ├───────────────┤ ├──────────────────────────────────┤ │
│ │ │ │ │ │
│ │ Агрегат: │ │ ┌─────────────────────────────┐ │ │
│ │ ☑ Кабина (12) │ │ │ Фара правая OEM 21467241 │ │ │
│ │ ☑ Шасси (8) │ │ │ 8 500 ₽ - 12 000 ₽ │ │ │
│ │ ☐ Силовой (15)│ │ │ 3 варианта │ │ │
│ │ ☐ Электрика(7)│ │ └─────────────────────────────┘ │ │
│ │ │ │ │ │
│ │ Узел: │ │ ┌─────────────────────────────┐ │ │
│ │ ☑ Оптика (4) │ │ │ Амортизатор OEM 20499340 │ │ │
│ │ ☐ Зеркала (2) │ │ │ 5 200 ₽ │ │ │
│ │ ☐ Подвеска(3) │ │ │ 2 варианта │ │ │
│ │ │ │ └─────────────────────────────┘ │ │
│ │ Состояние: │ │ │ │
│ │ ☑ Б/У (35) │ │ ... │ │
│ │ ☐ Новое (7) │ │ │ │
│ └───────────────┘ └──────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
"запчасти volvo фш4":
→ Search: title CONTAINS "volvo"
→ Suggestions: "Volvo FH4", "Volvo FM4"
→ Redirect: /zapchasti/volvo/fh4/
"фара вольво 4 фш":
→ Parse: "фара" + "вольво" + "4" + "фш"
→ Match: "Volvo FH4" (транслит: ФШ4)
→ Search: node=optics AND model=FH4
"volvo fh 2012":
→ Parse: brand=Volvo, model=FH, year=2012
→ Match: Volvo FH4 (year_start: 2012)
→ Redirect: /zapchasti/volvo/fh4/
"21467241":
→ Direct OEM search
→ Result: Фара правая OEM 21467241
"амортизатор вольво евро 6":
→ Parse: "амортизатор" + brand=Volvo + "euro6"
→ Filter: node=suspension AND platform=euro6
→ Results: FH4, FM4 амортизаторы
Index: products
Fields:
- field_oem (boost: 10)
- title (boost: 5)
- field_cross (boost: 8)
- field_models.name (boost: 6)
- field_models.field_transliteration (boost: 7)
- field_node.name (boost: 3)
Synonyms:
- "вольво" → "volvo"
- "фш" → "fh"
- "фм" → "fm"
- "сканиа" → "scania"
CREATE TABLE models (
id INT,
name VARCHAR(100), -- "Volvo FH4"
old_code VARCHAR(20), -- "4-FH" (для импорта)
generation_id INT,
platform VARCHAR(50), -- "euro6"
engines JSON, -- ["D13K", "D13C"]
year_start INT, -- 2012
transliteration VARCHAR -- "ФШ4"
);
drush scr import-nocodb-models.php
# → Создаёт taxonomy terms в Drupal
// ЭТАП 1: Структуризатор
foreach ($csv as $row) {
$models = parseCompatibility($row); // "4-FH" + "4-FM" → [FH4, FM4]
$node = determineNode($row['name']); // "Фара" → Оптика
$compat_type = $node->aggregate->compat_type; // "model"
$models = expandCompatibility($models, $compat_type); // расширение
createProduct($row['OEM'], $models, $node);
}
// ЭТАП 2: Вариации (SKU)
foreach ($csv as $row) {
createVariation($row['OEM'], $row['SKU'], $row['price'], ...);
}
URL: /zapchasti/{brand}/{model}/
Facets:
- Агрегат (Кабина, Шасси...)
- Узел (Оптика, Подвеска...)
- Состояние (Б/У, Новое)
- Цена (range)
Product page:
URL: /zapchasti/{part-name}-{oem}/
Canonical: на себя
Совместимость: показать все модели из field_models
expandCompatibility()Версия: 1.0.0
Дата: 2026-02-12