Drupal → Способы реализации каталога с большим количеством атрибутов товара (теория)

10.06.2015

Решил собрать в одном месте способы реализации каталога с большим количеством атрибутов товара (как на Яндекс.Маркете), их плюсы и минусы.

1. Один тип материала со всеми возможными полями

Создать один тип материала Товар, в который добавить все возможные поля. Категорию товара сделать полем таксономии. В форме товара показывать нужный набор полей с помощью своего аджаксового аналога Conditional Fields:

  • Тип материала Товар

    • Поле Категория
    • Поле Диагональ
    • Поле Операционная система
    • Поле Мощность
    • Поле Размер
    • ...

Плюсы:

  • Один тип материала. Легче создавать вьюхи, индексировать и т.п.
  • Легко вводить данные (готовые виджеты)
  • Относительно лёгкая темизация (уже готовые форматтеры)
  • Возможно использовать multi-value атрибуты
  • Относительно-простой способ создания поиска/фильтрации/фасетов (только в случае с заранее известным набором атрибутов)
  • Относительно-лёгкое добавление новых категорий и полей (нужны знания админки и соответствующие права)

Минусы:

  • Не подходит для реально большого числа атрибутов (больше нескольких десятков)
  • Не подходит когда список атрибутов заранее неизвестен (например при парсинге или импорте)
  • Много таблиц field_data_* и field_revision_*
  • Каждое поле — это дополнительный SQL запрос при отсутствующем кэше
  • Тяжело добавлять зависимость поля от категории

2. Множество типов материала с разными наборами полей

Для каждой категории создать свой тип материала (или сущности product, если речь о Drupal Commerce) со своим набором полей.

  • Тип материала Планшеты

    • Поле Диагональ
    • Поле Операционная система
    • ...
  • Тип материала Колонки
    • Поле Мощность
    • Поле Размер
    • ...

Плюсы:

  • Можно использовать одно поле в нескольких категориях (например поле "Вес" будет почти везде)
  • Легко вводить данные (готовые виджеты)
  • Относительно лёгкая темизация (уже готовые форматтеры)
  • Возможно использоваться multi-value атрибуты
  • Относительно-простой способ создания поиска/фильтрации/фасетов (только в случае с заранее известным набором атрибутов)

Минусы:

  • Не подходит для реально большого числа атрибутов (больше нескольких десятков)
  • Не подходит когда список атрибутов заранее неизвестен (например при парсинге или импорте)
  • Много таблиц field_data_* и field_revision_*
  • Нет базового типа материалов, по которому можно строить Views, делать темизацию и т.п. (хотя здесь можно опираться на префикс бандла — например product_)
  • Тяжело добавлять новые категории — нужно создать тип материала, добавить поля, настроить форматтеры, настроить views и прочее

3. Свой тип сущности с множеством свойств в качестве атрибутов

Создать с помощью Entity API свой тип сущности product с множеством свойств. Каждое свойство (атрибут) — поле таблицы. Категорию товара сделать полем таксономии, как в первом способе:

  • Сущность Товар

    • Поле Категория
    • Свойство Диагональ
    • Свойство Операционная система
    • Свойство Мощность
    • Свойство Вес
    • ...

Плюсы:

  • Все данные в одной таблице

Минусы:

  • Не подходит для реально большого числа атрибутов
  • Не подходит когда список атрибутов заранее неизвестен (например при парсинге или импорте)
  • Кодинг
  • Относительно-сложно вводить данные (нет виджетов)
  • Сложная темизация (нет форматтеров)
  • Тяжёлое добавление новых полей (изменить схему бд, добавить виджет в форму, вывести значение)
  • Невозможно использовать multi-value атрибуты, придётся делать их полями или кодить логику вручную
  • Придётся помучаться с поиском/фильтрацией

4. Составное multi-value поле из двух полей — атрибут, значение (EAV)

Добавить к типу материала составное multi-value поле Атрибуты, в котором можно будет выбрать атрибут и ввести его значение:

Такой способ называется EAV (Entity–attribute–value) и используется в OpenCart и многих других CMS.

Название атрибута может быть как термином таксономии, так и просто строкой.

Поле можно написать либо самому, либо воспользоваться такими модулями как Field collection, Multifield, Double field. Для Drupal 8 можно использовать мой модуль EAV Field.

Плюсы:

  • Подходит для большого числа атрибутов
  • Подходит когда список атрибутов заранее неизвестен
  • Все данные в одной таблице
  • Условно-возможно использовать multi-value атрибуты (просто один атрибут будет указан несколько раз)
  • Относительно-лёгкое добавление новых категорий

Минусы:

  • Кодинг
  • Все значения атрибутов будут одного типа — строки. Хотя можно придумать костыли.
  • Относительно-сложно вводить данные (нет виджетов)
  • Относительно-сложно темизировать (нет форматтеров)
  • Сложно реализовать поиск/фильтрацию по атрибутам

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

Кто сталкивался с подобной задачей, напишите свой способ решения.

Похожие записи

Комментарии

Гость
10.06.2015, 21:50

Пользовался вторым вариантом, но эт то-же мрак. Неподготовленному пользователю не доверишь добавление новых типов. И очень сложно добавлять все свойства. Особенно когда для разных типов товаров свойство называется одинаково а параметры разные.

Гость
11.06.2015, 10:52

У вас есть реальные примеры чего-то похожего на Маркет Яндекса, где работал бы какой-то из перечисленных вариантов? Когда сотни типов товаров и у каждого порядка сотни характеристик, по которым будет идти фильтрация.
Думаю что для реализации подобного придется придумать вариант 5.

Сергей
11.06.2015, 13:23

Извини, что наглею, но если поиск с фасетами прикручивать то какой вариант лучше с точки зрения производительности. И с точки зрения кеширования, какой вариант лучше?

фасеты из коробки будут работать только с первым и вторым способом

Андрей
12.06.2015, 15:26

Пользуюсь способами 1, 2.
Остальные способы, как на меня, имеют недостатки которые перекрывают все "+":

  • Относительно-сложно вводить данные (нет виджетов)
  • Относительно-сложно темизировать (нет форматтеров)
  • Сложно реализовать поиск/фильтрацию по атрибутам
    • То есть, если много полей в товаре, и разработчик пользуется способом 3, 4 - придется много кодить и решать выше перечисленые проблемы.
      Тогда возникает вопрос о целесообразности использования самого Друпала, как инструмента, для создания оного каталога / магазина.
      Для меня идеальным "рецептом" является способ 1 и 2 + продвинутое кеширование.

Как правило на больших сайтах ничего не правят вручную, всё импортируется из внешнего источника, поэтому виджеты не так важны. Ну а заставить search api индексить данные из составного поля я думаю как-то можно, после этого и фасеты сами заработают.

Анатолий
14.06.2015, 08:46

Скажи, пожалуйста, как вы сделали на указанном вами сайте КТС, чтобы в поиске при вводе части слова выскакивало не только описание возможного товара но и картинка?
Спасибо.

У меня такое на шестерке реализовано вот тут http://podshipnik-servis.ru

Тип материала Товар, 40 общих полей для всех товаров.
Самописный модуль клонирования типов материалов. Когда надо, клонирую тип Товар и добиваю новыми полями и группами. Точнее, это манагеры делают.
Сделано несколько шаблонных Views по полям типа Товар.
Самописный модуль клонирования Views, которому можно указать тип товара и он добавит оттуда все дополнительные для данного товара поля во вьшку и раскрытые фильтры к ней.

Так и живем уже три года. :)

Денис
01.07.2015, 11:08

Перепробовал все способы, пришлось придумать свой. Т.к. сайт не только каталог, но и магазин, то был взят за основу комерс, сразу с со своим энтити product. На каждую категорию товаров создавался свой тип продукта со своими полями. У редакторов есть возможность создавать поля самим. Так же поля переиспользовать в других типах.
Это дает возможность использовать выборки по парент ноде и везде единообразно работать с нодой produc_display.
Дальше идет магия.
Словарь таксономии - каталог содержит в себе спец поле - тип продукта, в котором указывается какой тип продукта будет тут отображаться, используется в форме редактирования: при выборе термина сразу автоматом грузится форма продукта с нужным типом. А во вторых используется при выводе: при отображении этого термина можно получить все поля продукта и построить фильтры.
Фильтры работают на СерчАпи.
При редактировании термина таксономии выбираем тип продукта, тут же сразу отображаются все поля продукта и появляется возможноть выбрать тип виджета и фильтра в каталоге, его порядок и группу. Если выбран фильтр то автоматом создается индекс в поиске.
Ну и естественно кастомный функционал отображения, который учитывает все ранее введенное.
К сожалению, так и не придумал как это все сделать контрибовским модулем, т.к. слишком много зависимостей от требуемого функционала. Пример работы можно посмотреть здесь http://karcher.com.by/minimoyki
Пока реализовано 4 сайта на этом движке. Пока полет нормальный. Буду надеятся что найдется время перевести этот функционал в контриб.

Артур
01.07.2015, 23:01

Я написал кастомный модуль наподобие field_collection с той лишь разницей, что можно налету переключать пресеты полей. Подробнее:
- Новая сущность fieldset (по типу продукта в екомерс)
- к сущности можно добавляем полями любые нужные атрибуты
- Так же модуль предоставляет кастомное поле, которые привызывает наборы полей к ноде.
Работает это так:
- Создаём тип ноды "товар" с общими полями (категория, цена, фотки, пр..)
- добавляем наше хитрое поле к ноде
При создании ноды:
- В кастомном поле выбираем заранее созданий нами пресет полей
- сохраняем
- на странице ноды появяется вкладка (либо ссылка), которая открываем форму атрибутов.
- если в ноде потом при редактировании переключить пресет, то данные из старого пресета удаляются.

В итоге что-то среднее между первым и вторым способом.

@Артур а с поиском/фильтрацией как?

Артур
01.07.2015, 23:11

В планах есть задумка продумать модуль по действительно неограниченому кол-ву атрибутов (строки, списки, числа, диапазоны) и не плодить таблицы.
Как основная идея хранить всё в одном текстовом биг-поле в формате html таблицы. Реализовать форматтары для вывода и ввода данных - что не так сложно.

Почему html таблица: потому что структурированное хранение, доступный по умолчанию просмотр и хорошая дефолтная индексация.

Ещё не продумал как полноценно подружить с вьюсом: фильтрация и сортировка, главным образом останавливает(с выводом полей проблем нет). Пока единственная идея делать индексные таблицы, но.. и это меня пока останавливает :))

Артур
01.07.2015, 23:13

xandeadx, по сколько эту полноценные сущности и вообще друпал-вей, то я добавил поддержку вьюса для releationships - работает отлично.

Ну по сути точно такая же схема работает в drupal commerce - один product display и множество типов product со своими полями. Вариация второго способа.

Артур
01.07.2015, 23:30

xandeadx, по сути да, только у меня этот функционал можно ещё цеплять к разным типам нод.
У меня на сайте 3 каталога: Модели оборудования, Аксессуары, Расходные материалы.
У них разные "общие свойства", разный набор доступных пресетов и разный дизайн :)

На самом деле мне просто жутко не хотелось разбираться в екомерс с учётом того что стандартные его сценарии не устраивали.

пример рабочего каталога (здесь без фильтров, с фильтрами другой сайт в работе)
http://ergomax-rf.ru/equipment/1

Кстати, спасибо за помощь с модулем яндекс карт (я его на этот сайт и ставил)

Владимир
02.07.2015, 13:57

Использую второй способ, но с небольшим хаком. Я создал сущность типа product и сделал кодингом реализацию наследования, т.е. у меня в типе product референс ссылка на "материал товара". Мне это понадобилось вот зачем, во первых в сущность product я сваливаю поля, которые не нужны оператору, работающему с самим товаром, например: артикул (генерится автоматически), термины таксономии (присваиваются автоматически на основании значений полей), различные цены (от поставщиков они вводятся в валюте, а тут пересчитываются в рубли по курсу) и т.д. Также отпадает вышеописанная проблема вывода в вьюхах, не нужно фильтровать бандлы, можно создать обязательную бэк-зависимость от product.
В дополнение скажу, что сам не использую вьюхи напрямую, использую поиск Solr и фасеты, гораздо удобнее всё пропустить через поисковой движок и пользоваться уже так сказать простыми поисковыми оттисками от сложных товаров с множеством полей.
Проблему множества типов товаров не решал, у меня их порядка 10, полей порядка 20 у каждого.

Делал так:
1. Field Collection состоящий из двух полей: "key" и "value" - оба поля термины таксономии
2. Написал импорт с помощью Feeds - несуществующие параметры создаются автоматически
3. Написал индексацию данных параметров Search Api
4. Написал свой форматер для формирования урлов :)

3. Написал индексацию данных параметров Search Api

как это работает? вкратце

Костылем :)

<?php

class SearchApiAlterAddFC extends SearchApiAbstractAlterCallback {
  /**
   * Alter items before indexing.
   *
   * Items which are removed from the array won't be indexed, but will be marked
   * as clean for future indexing. This could for instance be used to implement
   * some sort of access filter for security purposes (e.g., don't index
   * unpublished nodes or comments).
   *
   * @param array $items
   *   An array of items to be altered, keyed by item IDs.
   */
  public function alterItems(array &$items) {
    if (!$items) {
      return;
    }

    foreach ($items as $item) {
      $wrapper = $this->index->entityWrapper($item);
      if (count($item->field_product)) {
        foreach ($wrapper->field_product->field_params as $param) {
          $key = $param->field_key->name->value();
          $key = str_replace(' ', '_', mb_strtolower($key));
          $key = transliteration_get($key);
          $value = $param->field_value->value();
          $item->{'addmi_' . $key} = $value;
        }
      }
    }

    return;
  }

  public function propertyInfo() {
    $terms = taxonomy_get_tree(2);

    foreach ($terms as $term) {
      $ret['addmi_' . str_replace(' ', '_', transliteration_get(mb_strtolower($term->name)))] = array(
        'label' => 'addmi_' . str_replace(' ', '_', transliteration_get(mb_strtolower($term->name))),
        'description' => 'Param for facets',
        'type' => 'taxonomy_term',
      );
    }
    return $ret;
  }


}

При включении добавляет новый фильтр, который в свою очередь добавляет все поля из словаря "Params key" в поля для индексации.
Потом просто отмечаешь необходимые - индексируешь, и создаешь фасеты.

Вариант с текстовыми полями пробовал - не подошел по причине невозможности сделать красивые урлы.

По сути это реализация моего комментария. Если атрибутов будет не сильно много (условно до сотни) то можно написать админку управления атрибутами (название, тип, вес и т.д.) и скрыть под капотом процесс добавления атрибута в индекс, создания фасета, вывода блока. Должно получится покрасивее.

Сейчас они и добавляются автоматом... Но вывод блока лучше таки вручную делать, т.к. при импорте разные косяки бывают ))

P.s. Не по теме, но отпишусь: совсем недавно стал использовать
Panels + Omega + Panels Everywhere
Очень рекомендую :) Сам три года не хотел их ставить, но сейчас могу сказать, что одни только плюсы.

Для 1 варианта как лучше или проще сделать фильтрацию по полям, чтобы для каждого типа товара выходили свои фильтры?

Попробовал модуль Views dependent filters вместе с Better Exposed Filters, не работает, дает ошибку, что еще можно попробовать? Или делать разные вьюшки для разных типов товара?

1. По факту работал с магазином в котором тысячи товаров и сотни атрибутов. Все это было в одно типе товара. В итоге имеем сотни джоинов, которые убивают сайт.
2. Вариант когда множество типов товаров, но ограниченное число атрибутов. Причем все значения атрибутов заталкиваются в несколько словарей. В итоге разросшийся словарь таксономии, который съедает всю php память, т.к. все tids заталкиваются в allowed values поля. Думаю что если поля будут текстовые, то проблема с производительностью вылезет позже. Есть сайт, где множество типов товаров и множество атрибутов. Т.к. они текстовые, то проседает не так сильно. Вопрос с фасетными фильтрами решается своим велосипедом.
3. Вот теперь смотрю на 4 вариант и думаю какие сюрпризы он может преподнести кроме очевидного (велосипед для фасетных фильтров).
Первый и второй вариант работали с автосозданием полей и типов материалов.
P.s.
В статье не хватает тэга commerce, чтобы можно было ее найти.

univerico
14.08.2018, 13:17

Смешанный способ:
Все как в 4, но помещается это втип поля с типом ссылка на сущность от модуля Inline Entity Form (описано Orion76 на drupal.ru).
(Хотя не знаю, может это и свопадает с чем-то что уже раньше описано)

Может немного не про то, но все же. Добавляем поле «Особенности» - ссылка на термин, добавляем все возможные особенности товара. Далее, для каждой категориии товара создаём свой Views с собственными фильтрами. С помощью BEF можно сделать чекбокмы для раскрытых фильтров. На небольшом количестве товаров и небольшом количестве параметров, работает «на ура»)

у "особенности" должно быть какое-то значение, как его вводить и где его хранить?

Теперь понял) В моем варианте любая «Особенность» будет иметь только значения «есть» или «нет».

Добавить комментарий