В данной публикации я обзорно рассмотрю структуры данных Magento 2, поддерживающих функционирование такой концепции, как EAV. Разработчикам иногда нужно выбираться из дебрей кода и пытаться обозревать места своего жизнепровождения с высоты полёта орла — это позволяет наводить фокусы на вещи действительно важные или просто большие. Вот я и выбрался.

Аббревиатура EAV раскрывается как Entity-Attribute-Value (это для тех, кто не ходил по ссылке выше). Основной "плюшкой" EAV является эффективное использование пространства БД в тех случаях, когда возможное количество различных атрибутов (свойств, параметров), которые могут быть использованы для описания вещей (сущностей), является очень широким, но количество атрибутов, которое на самом деле относится к отдельному объекту, является относительно небольшим. Хорошим примером подобного случая в e-commerce служит такое понятие, как "продукт" — значимые атрибуты продуктов "телевизор" (размер экрана) отличаются от значимых атрибутов продуктов "спальный мешок" (минимальная комфортная температура).
Так что же Magento 2 предлагает для хранения данных в формате EAV?
'eav_' namespace
В свежеразвёрнутой базе Magento 2.3 есть 21 таблица с префиксом eav_. Все их можно разделить на три группы:
eav_attributeeav_entityeav_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 предлагает такой вариант для следующих восьми сущностей:
customercustomer_addresscatalog_categorycatalog_productorderinvoicecreditmemoshipment
'eav_attribute': реестр атрибутов
Следующий шаг — находим, какими атрибутами могут характеризоваться эти типы сущностей. Данная информация находится в таблице eav_attribute. Реестр атрибутов имеет замыкание на реестр типов сущностей по внешнему ключу (foreign key). В реестре атрибутов изначально 135 записей, принадлежащих 4 типам сущностей:
customercustomer_addresscatalog_categorycatalog_product
О чём это говорит? Ну, хотя бы о том, что остальные типы сущностей:
orderinvoicecreditmemoshipment
не используют EAV-структуру для хранения данных. То есть, на каком-то этапе энтузиазм присутствовал и использование EAV планировалось для восьми типов сущностей, но по факту остановились на 4.
'eav_entity_': пространство-призрак
Пространство таблиц eav_entity напоминают китайские города-призраки — из 9 таблиц пространства данные содержатся только в двух:
eav_entity_type: это реестр типов сущностей, о которых я упоминал выше;eav_entity_attribute: используется для упорядочивания атрибутов в группах (ближе к отображению данных, чем к их хранению); данная информация больше относится непосредственно к самим атрибутам, чем к сущностям (т.е., явно не из этого прихода — ей место в пространствеeav_attribute_);
Остальные 7 таблиц — пусты:
eav_entityeav_entity_datetimeeav_entity_decimaleav_entity_inteav_entity_storeeav_entity_texteav_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_, ...) а суффикс — одному из типов данных:
datetimedecimalinttextvarchar
Т.е., значения для атрибута #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_datetimecustomer_entity_decimalcustomer_entity_intcustomer_entity_textcustomer_entity_varcharcustomer_address_entity_datetimecustomer_address_entity_decimalcustomer_address_entity_intcustomer_address_entity_textcustomer_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_seteav_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.