xandeadx.ru Блог музицирующего веб-девелопера

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

Опубликовано в

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

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.

Плюсы:

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

Минусы:

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

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

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

P.S: статья лежала в черновиках два года :)

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

Комментарии RSS

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

Если ревизии не нужны то можно удалить лишние таблицы
https://www.drupal.org/project/field_sql_norevisions

Пользовал 2, 3 и к 4му добавил бы https://www.drupal.org/project/properties (грабли с импортом)
кодить приходится везде, ибо логика везде своя

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

http://www.ktc-ua.com/
работает на втором способе

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

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

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

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

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

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

это не я делал

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Использую второй способ, но с небольшим хаком. Я создал сущность типа 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, чтобы можно было ее найти.

Оставить комментарий

Содержимое этого поля является приватным и не будет отображаться публично. Если у вас есть аккаунт в Gravatar, привязанный к этому e-mail адресу, то он будет использован для отображения аватара.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступные HTML теги: <a> <i> <b> <strong> <code> <ul> <ol> <li> <blockquote> <em> <s>
  • Строки и параграфы переносятся автоматически.
  • Подсветка кода осуществляется с помощью тегов: <code>, <css>, <html>, <ini>, <javascript>, <sql>, <php>. Поддерживаемые стили выделения кода: <foo>, [foo].

Подробнее о форматировании