Magento2の商品関連のテーブルをまとめた

この記事は[Magento Advent Calendar 2017](https://adventar.org/calendars/2349)の14日目です。

# 動機

* 備忘録
* 社内の人間のテーブル構造を理解してもらいたい

# 商品周りのテーブル構造について

## 所感
たまに大変なことがある。

### ここが良くない
* EAVパターンは良くない
* DDL上で外部キーが貼ってあったりなかったり
* DDL上で外部キーが貼れないような外部キーの使われ方がされている
 * 後述するがcatalog_product_entity_intとeav_attribute_option_value等
* primary keyの名前に一貫性がない。テーブル名+id等なら良いし、entity_idなら良いがテーブルによってまちまち。

## 商品
* catalog_product_entity

商品IDがentity_idでSKUがくっついている。
すべての商品について持っていて、商品ごとにtype_idが文字列で入っていて、
それによって単品かセット商品か、コンフィグ商品かを区別している。
has_options、required_optionsで商品オプションについて存在するかが表現されているが、コンフィグ商品の場合には絶対に1にはならない、などがありカラム間で依存性がある。

### 商品関連テーブル
#### 商品とセット商品
catalog_product_entityにtype_idがbundleで入力される。
その上で下記の関連するテーブルにレコードが入力される。

* catalog_product_bundle_option         
* catalog_product_bundle_option_value   

セット商品の場合には二つ以上の組み合わせを選ぶことになる。
仮に服を考えるときに、トップとボトムのセットを仮定する場合に、
「トップ」「ボトム」といった区分がここに入力される。
catalog_product_bundle_optionは組み合わせ自体のIDと組み合わせの選択方法(セレクトメニューやテキスト入力など)が保持される。
catalog_product_bundle_option_valueには、「トップ」「ボトム」といった組み合わせに付随する情報が保存される。

* catalog_product_bundle_selection      

それぞれの組み合わせ、つまり「トップ」「ボトム」に属する商品が入力される。
catalog_product_bundle_option.option_idがoption_idとして、
option_id下につく商品IDがproduct_idとして入力される。
parent_product_idという形で親商品IDももっている。(catalog_product_bundle_optionにも当然ある。

* catalog_product_bundle_selection_price
* catalog_product_bundle_price_index 

セットでの特別価格的に使うテーブルに見受けられるが自分の環境では使用していないので不明。

#### 商品とオプション商品
商品に紐付いて選択可能なオプションを提示する。オプション自体は商品としては扱わない。

* catalog_product_option
* catalog_product_option_title

商品IDを持って、商品テーブルにたいして1:Nなレコードを作成する。
typeにはオプションの選択形式が文字列で入力され(テーブル作って外部キー化した方が良いと思う)、
一部の入力形式用のバリデーションが入力される。NULL値が入る設計。
catalog_product_option_titleにはオプションの名前が入る。

* catalog_product_option_type_value

catalog_product_optionにたいして1:Nなオプション選択項目が入力される。

* catalog_product_option_type_title

catalog_product_option_type_valueのprimary keyを外部キーとして、
1:1関係(正確にはstoreがあるので1:nになるが)の、テーブルで、
catalog_product_option_type_valueの表示名が入力される。
catalog_product_option_type_valueの子のテーブルになるので、
テーブル名はcatalog_product_option_type_title_value
とかになってる方が良いかと思います。

* catalog_product_option_type_price

catalog_product_option_type_titleと同様、価格が存在すれば入力される。
1:1というよりは1:0or1といった形のテーブルとなっている。

#### 商品とコンフィグ商品
商品に紐付いて選択可能なオプションを提示する。オプション自体が別の商品である。
イメージはセット商品で選ぶものが一つしかないような形。

* catalog_product_super_attribute

α商品の下にA,B,C...商品と紐づく形になる。
A,B,C...商品が何の属性を以ってα商品に紐づくかを表現する。
管理画面での設定画面では、特定のeav_attributeの1レコードを選択(同画面で作成もできる)をして、
親商品IDと属性IDの組み合わせテーブルとしてレコードが作成される。

* catalog_product_entity_int

子商品に上記で指定した属性値が付与される。
attribute_idには、対象となるeav_attribute_idが入力される
valueにはeav_attribute_optionのoption_idが入力される(!?)

* eav_attribute

名の通りEAV的に属性値を持たせるテーブル。
問題としては、商品のみならず、カテゴリやら顧客やらに属性を持たせるときにも同じように使う。

* eav_attribute_option
* eav_attribute_option_value

eav_attributeで定義した属性の下になる値を持たせる。
eav_attribute_optionはレコードのみを持ち、eav_attribute_option_valueに具体的な名前が入力される。
eav_attributeに仮にcolorを作成し、red,brownを作成したい場合には
eav_attribute_optionが2レコード作成される。
eav_attribute_option_valueには、eav_attribute_optionのprimary keyと、それぞれの色の名前を持つレコードが作成される。

* catalog_product_super_link

親商品と子商品がどのように紐づくかが記述される。

#### 商品テーブルと属性
* catalog_product_entity_datetime
* catalog_product_entity_decimal
* catalog_product_entity_gallery
* catalog_product_entity_text
* catalog_product_entity_varchar
* catalog_product_entity_int

catalog_product_entity.entity_idとeav_attribute.attribute_idで関係テーブルを持たせている。
eav_attributeには値自体の型を持っていてそれにしたがって、上記テーブル群に振り分けをして入力される。
value部分に具体的な値を持つことになる。
とある商品にたいして、発売開始日が2017年1月1日、みたいなものを持たせたい場合には、
該当商品ID、発売開始日属性レコード、2017-01-01をそれぞれ、entity_id,attribute_id,valueに入力することになる。

例外としては、上記のコンフィグ商品の属性と属性値のように、
catalog_product_entity_intでレコードを持ち、valueにeav_attribute_optionのoption_idを保持する形がある。


* catalog_product_entity_media_gallery
* catalog_product_entity_media_gallery_value
* catalog_product_entity_media_gallery_value_to_entity
* catalog_product_entity_media_gallery_value_video

商品画像など。