В данной публикации я обзорно рассмотрю структуры данных Magento 2, поддерживающих функционирование такой концепции, как EAV. Разработчикам иногда нужно выбираться из дебрей кода и пытаться обозревать места своего жизнепровождения с высоты полёта орла — это позволяет наводить фокусы на вещи действительно важные или просто большие. Вот я и выбрался.
Аббревиатура EAV раскрывается как Entity-Attribute-Value (это для тех, кто не ходил по ссылке выше). Основной "плюшкой" EAV является эффективное использование пространства БД в тех случаях, когда возможное количество различных атрибутов (свойств, параметров), которые могут быть использованы для описания вещей (сущностей), является очень широким, но количество атрибутов, которое на самом деле относится к отдельному объекту, является относительно небольшим. Хорошим примером подобного случая в e-commerce служит такое понятие, как "продукт" — значимые атрибуты продуктов "телевизор" (размер экрана) отличаются от значимых атрибутов продуктов "спальный мешок" (минимальная комфортная температура).
Так что же Magento 2 предлагает для хранения данных в формате EAV?
'eav_' namespace
В свежеразвёрнутой базе Magento 2.3 есть 21 таблица с префиксом eav_
. Все их можно разделить на три группы:
eav_attribute
eav_entity
eav_form
Проще всего с eav_form
— эти таблицы относятся к отображению некоторых EAV-данных на UI и непосредственно к размещению EAV-данных в базе не относятся (я рассматриваю только структуры данных и только с точки зрения хранения информации, а не её отображения). Для эксперимента я удалил таблицы пространства eav_form
из базы и это не помешало мне оформить заказ в магазине. Так что нужно ещё поискать, где используются данные из этого пространства таблиц и насколько они нужны для функционирования Magento.
Из оставшихся двух группа eav_attribute
относятся к букве A(ttribute), а группа eav_entity
— к букве E(ntity). Где же буква V(alue)?
Значения для атрибутов сущностей нужно искать в суффиксах имён таблиц:
_datetime
_decimal
_int
_text
_ varchar
Можно видеть, что подобные суффиксы имеются у таблиц, начинающихся с:
catalog_category_entity_
catalog_product_entity_
customer_address_entity_
customer_entity_
eav_entity_
Простое перемножение кол-ва суффиксов (5) на кол-во префиксов (5) даёт нам общее кол-во таблиц (25) в которых предполагается хранение values-данных.
'eav_entity_type': реестр типов сущностей
Начало EAV в Magento нужно искать в таблице eav_entity_type
. Именно здесь задаётся, для каких типов сущностей значения атрибутов будут сохранятся в EAV-структуре. Так вот, изначально Magento 2.3 предлагает такой вариант для следующих восьми сущностей:
customer
customer_address
catalog_category
catalog_product
order
invoice
creditmemo
shipment
'eav_attribute': реестр атрибутов
Следующий шаг — находим, какими атрибутами могут характеризоваться эти типы сущностей. Данная информация находится в таблице eav_attribute
. Реестр атрибутов имеет замыкание на реестр типов сущностей по внешнему ключу (foreign key). В реестре атрибутов изначально 135 записей, принадлежащих 4 типам сущностей:
customer
customer_address
catalog_category
catalog_product
О чём это говорит? Ну, хотя бы о том, что остальные типы сущностей:
order
invoice
creditmemo
shipment
не используют EAV-структуру для хранения данных. То есть, на каком-то этапе энтузиазм присутствовал и использование EAV планировалось для восьми типов сущностей, но по факту остановились на 4.
'eav_entity_': пространство-призрак
Пространство таблиц eav_entity
напоминают китайские города-призраки — из 9 таблиц пространства данные содержатся только в двух:
eav_entity_type
: это реестр типов сущностей, о которых я упоминал выше;eav_entity_attribute
: используется для упорядочивания атрибутов в группах (ближе к отображению данных, чем к их хранению); данная информация больше относится непосредственно к самим атрибутам, чем к сущностям (т.е., явно не из этого прихода — ей место в пространствеeav_attribute_
);
Остальные 7 таблиц — пусты:
eav_entity
eav_entity_datetime
eav_entity_decimal
eav_entity_int
eav_entity_store
eav_entity_text
eav_entity_varchar
Очень похоже на попытку унификации способа хранения значений для атрибутов сущностей в одном наборе таблиц (datetime
, decimal
, int
, text
, varchar
) вместо того, чтобы иметь по 5 таблиц с соответствующими суффиксами для каждого типа сущностей. На неудачную попытку? Или это будущее EAV в Magento?
По-любому, Земля же была безвидна и пуста, и тьма над бездною, и Дух Божий в настоящем эти таблицы изначально не используются.
Типы значений атрибутов
В таблице eav_entity_type
задаются типы сущностей, в таблице eav_attribute
задаются сами атрибуты и их привязка к соответствующим типам сущностей. А как определить, где искать значение для такого-то атрибута такой-то сущности?
В этом нам поможет поле eav_attribute.backend_type
. Оно показывает, где сохраняются значения атрибутов:
- static: в таблице с данными о самой сущности (например, значения для атрибута #9 —
customer.email
, нужно искать в таблице клиентовcustomer_entity
в столбцеemail
);
Для остальных типов значения сохраняются в отдельных таблицах, в названиях которых префикс соответствует типу сущности (customer_
, ...) а суффикс — одному из типов данных:
datetime
decimal
int
text
varchar
Т.е., значения для атрибута #79 catalog_product.special_from_date
типа datetime
сохраняются в таблице catalog_product_entity_datetime
. Значения для атрибута #77 catalog_product.price
— в таблице catalog_product_entity_decimal
.
Что можно интересного увидеть в таблице eav_attribute
в связи с типами значений? Как я уже отмечал выше, в данной таблице описаны атрибуты только для 4 типов сущностей из 8, зарегистрированных в eav_entity_type
. При этом для сущностей типа customer
и customer_address
все атрибуты, определённые изначально, имеют тип значений static
— т.е., являются обычными колонками в таблице и никак не используют преимуществ EAV-подхода. Таблицы:
customer_entity_datetime
customer_entity_decimal
customer_entity_int
customer_entity_text
customer_entity_varchar
customer_address_entity_datetime
customer_address_entity_decimal
customer_address_entity_int
customer_address_entity_text
customer_address_entity_varchar
изначально пусты и могут быть использованы только программным путём (т.е., через админку, без сторонних расширений, нет возможности записать что-либо в эти таблицы).
EAV для категорий
Категории каталога — вот первая сущность, которая более-менее использует EAV подход в Magento. Тип сущности — catalog_category
, всего изначальных атрибутов — 30, из которых не-статических — 26. То есть, значения только 4 атрибутов (children_count
, level
, path
, position
) сохраняются в таблице catalog_category_entity
, остальные сохраняются в наборе таблиц catalog_category_entity_
[ datetime
| decimal
| int
| text
| varchar
].
Структура таблиц из этого набора очень похожа как друг на друга, так и на аналогичные таблицы других типов сущностей (клиентов, их адресов и т.д.):
CREATE TABLE `catalog_category_entity_datetime` (
`value_id` int(11) NOT NULL AUTO_INCREMENT,
`attribute_id` smallint(5) unsigned NOT NULL DEFAULT '0',
`store_id` smallint(5) unsigned NOT NULL DEFAULT '0',
`entity_id` int(10) unsigned NOT NULL DEFAULT '0',
`value` datetime DEFAULT NULL,
PRIMARY KEY (`value_id`),
UNIQUE KEY `...` (`entity_id`,`attribute_id`,`store_id`),
...
) ...
Для различных типов сохраняемых значений (datetime
, decimal
, int
, text
, varchar
) меняется только тип колонки value
. Данная структура позволяет сохранять отдельное значение (value
) отдельного атрибута (attribute_id
) отдельной сущности (entity_id
) для отдельной витрины (store_id
).
В связи с архитектурными особенностями Magento добавляется дополнительная связь с витриной — store_id
. Таким образом возможна локализация значений одного и того же атрибута одной и той же сущности для различных витрин. Категории каталога — это первые сущности в Magento, для которых можно использовать EAV-подсистему прямо "из коробки". Задавать значения для атрибутов каталогов можно через админку.
Можно не только давать различные значения для текстовых атрибутов, переводя на язык соответствующей витрины, но и локализовывать атрибуты других типов. Например, в преддверии рождественских праздников на ru-витринах магазина для атрибута catalog_category.custom_design_from
можно выставить значения 7-го января следующего года, а на en-витринах — 24-го декабря этого.
EAV для продуктов
В общем-то, это тот самый тип сущностей, ради которого EAV и затевалась в Magento. Тип сущности — catalog_product
, всего изначальных атрибутов — 63, из которых не-статических — 56. Структура таблиц, поддерживающих EAV для продуктов, аналогична структуре таблиц для каталогов. Но есть одно значительное отличие. Для продуктов можно создавать новые атрибуты через админку — это default'овый функционал Magento, из коробки. Если для других сущностей Magento предоставляет только EAV структуры данных в расчёте на их программное заполнение, то для продуктов реализован интерфейс, позволяющий делать это на уровне пользователя (управляющего магазином) — Stores / Attributes / Product.
Для продуктов задействованы ещё две таблицы, относящиеся к EAV:
eav_attribute_set
eav_attribute_group
По-большому счёту, они скорее относятся к отображению информации, чем к её хранению. Атрибуты продукта объединяются в наборы (set
) и при создании продукта ему присваивается набор атрибутов, что позволяет при заполнении карточки продукта для, например, телевизора, выбирать набор атрибутов, относящийся именно к бытовой технике (или даже для группы продуктов "телевизоры"). Объединение атрибутов в наборы происходит в Stores / Attributes / Product / Attribute Set:
Итого
IMHO, Magento является хорошим примером того, что целесообразность применения EAV достаточно узкая. При закладке на использование EAV для 8 сущностей (eav_entity_type
) EAV-нотация используется только для 4 сущностей (eav_attribute
), из которых только 2 сущности имеют действительно EAV-атрибуты — catalog_category
и catalog_product
. Причём для catalog_category
EAV-атрибуты используются не по своему прямому назначению (большое количество различных атрибутов для описания сущности при малом количестве атрибутов, относящихся к отдельному экземпляру), а для "по-витринной локализации" значений атрибутов (один и тот же набор атрибутов для сущности "категория каталога" с возможностью атрибута экземпляра иметь различные значения для различных витрин магазина).
Полноценное использование EAV применяется только для catalog_product
(правда и тут есть примесь "по-витринной локализации", но это уже расширение EAV-модели, а не её профанация, как в случае с категориями). Зато с продуктами Magento раскрывает EAV по полной — Magento-приложение можно смело использовать для наглядной демонстрации принципов EAV.