bagisto-attribute-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Attribute Development

属性开发

Bagisto stores product data as EAV — entity, attribute, value — rather than as columns on
products
. A product row carries almost nothing; its name, price, description and every custom field live in
product_attribute_values
, one row per attribute per locale per channel.
This is the concept most Bagisto mistakes trace back to, so it is worth reading eav.md before changing anything that touches product data.
Bagisto采用EAV(实体-属性-值)模式存储产品数据,而非将数据作为
products
表的列存储。产品表的行几乎不承载实际数据;产品名称、价格、描述及所有自定义字段都存储在
product_attribute_values
表中,每个属性、每个区域、每个渠道对应一行数据。
这是Bagisto多数问题的根源,因此在修改任何涉及产品数据的内容前,建议先阅读eav.md文档。

Reference files

参考文件

FileLoad when
eav.mdHow values are stored and resolved — the column map, scope, the flat index
attributes.mdCreating attributes, families, groups, options, validation and swatches
文件适用场景
eav.md值的存储与解析方式——列映射、范围、扁平索引
attributes.md创建属性、属性族、属性组、选项、验证及色板(swatch)

The shape

层级结构

attribute_families            a product type's whole form  (e.g. "Default")
  └── attribute_groups        a tab/section within it      (e.g. "General")
        └── attributes        a field                      (e.g. "name")
              └── attribute_options   for select, multiselect and checkbox
A product belongs to one family, and that family decides which attributes it has.
AttributeFamily::custom_attributes()
and
AttributeGroup::custom_attributes()
are the relations that walk it.
attribute_families            产品类型的完整表单(例如“默认”)
  └── attribute_groups        表单内的标签页/分区(例如“常规”)
        └── attributes        具体字段(例如“名称”)
              └── attribute_options   用于单选、多选和复选框的选项
一个产品属于一个属性族,该属性族决定了产品拥有哪些属性。
AttributeFamily::custom_attributes()
AttributeGroup::custom_attributes()
是遍历该层级的关联方法。

The column map

列映射

An attribute's
type
decides which column of
product_attribute_values
holds its value:
typecolumn
text
,
textarea
,
multiselect
,
checkbox
,
file
,
image
text_value
price
float_value
boolean
boolean_value
select
integer_value
date
date_value
datetime
datetime_value
This map lives on
Attribute::$attributeTypeFields
, and the model exposes the resolved name as
$attribute->column_name
. Never guess the column — read it from the attribute, or a
select
silently writes into
text_value
and reads back as null.
AttributeTypeEnum
is the authoritative list of types;
ValidationEnum
(
numeric
,
email
,
decimal
,
url
,
regex
) and
SwatchTypeEnum
(
dropdown
,
color
,
image
,
text
) cover the rest.
属性的
type
决定了其值存储在
product_attribute_values
表的哪一列:
类型列名
text
,
textarea
,
multiselect
,
checkbox
,
file
,
image
text_value
price
float_value
boolean
boolean_value
select
integer_value
date
date_value
datetime
datetime_value
该映射定义在
Attribute::$attributeTypeFields
中,模型通过
$attribute->column_name
暴露解析后的列名。切勿猜测列名,应从属性中读取,否则单选框(select)会静默写入
text_value
列,读取时返回null。
AttributeTypeEnum
是权威的类型列表;
ValidationEnum
numeric
,
email
,
decimal
,
url
,
regex
)和
SwatchTypeEnum
dropdown
,
color
,
image
,
text
)覆盖其他相关类型。

Scope: the two flags that cause most bugs

范围:引发最多bug的两个标志

Every attribute carries
value_per_locale
and
value_per_channel
. Together they decide how many rows a single attribute has for one product, and which one a read returns:
value_per_channel
value_per_locale
Rows per product
falsefalse1
falsetrueone per locale
truefalseone per channel
truetrueone per channel per locale
Product::getCustomAttributeValue()
resolves the right row for the requested channel and locale, falling back to the default channel and locale when the requested one is empty. A value that "disappears" on a second locale is almost always an attribute written without honouring these flags.
The table enforces this with a unique index on
(channel, locale, attribute_id, product_id)
, so writing the wrong scope combination is a constraint violation rather than a silent duplicate.
每个属性都带有
value_per_locale
value_per_channel
标志。它们共同决定单个产品的单个属性对应多少行数据,以及读取时返回哪一行:
value_per_channel
value_per_locale
每个产品的行数
falsefalse1
falsetrue每个区域一行
truefalse每个渠道一行
truetrue每个渠道每个区域一行
Product::getCustomAttributeValue()
会为请求的渠道和区域解析正确的行,当请求的行为空时,会回退到默认渠道和区域。某个值在第二个区域“消失”,几乎总是因为写入属性时未遵循这些标志。
表中通过
(channel, locale, attribute_id, product_id)
的唯一索引来强制该规则,因此错误的范围组合写入会触发约束冲突,而非静默生成重复数据。

Non-negotiables

必须遵守的规则

  • Go through the repository.
    AttributeRepository
    ,
    AttributeFamilyRepository
    ,
    AttributeGroupRepository
    ,
    AttributeOptionRepository
    — never write
    product_attribute_values
    by hand.
  • Read the column from the attribute, via
    column_name
    or
    $attributeTypeFields
    . A hard-coded column is a bug waiting for the first non-text attribute.
  • Honour
    value_per_locale
    and
    value_per_channel
    on every write
    , not just on read. Writing one row for an attribute scoped per locale loses every other locale's value.
  • A
    select
    /
    multiselect
    value is an option id
    , not the label.
    select
    stores one id in
    integer_value
    ;
    multiselect
    stores comma-separated ids in
    text_value
    .
  • Option labels are translatable — they live in
    attribute_option_translations
    , so a label added in one locale must be added in all 22.
  • Changing an attribute's
    type
    orphans its existing values
    , because the new type reads a different column. Treat it as a data migration, not an edit.
  • Reindex after a change that affects listing. Filterable and listing attributes are denormalised into
    product_flat
    by the flat indexer; until it runs, the grid and storefront show the old value.
REQUIRED SUB-SKILL: Use bagisto-change-verification before calling any change done.
  • 通过仓库操作:使用
    AttributeRepository
    AttributeFamilyRepository
    AttributeGroupRepository
    AttributeOptionRepository
    ——切勿手动写入
    product_attribute_values
    表。
  • 从属性中读取列名:通过
    column_name
    $attributeTypeFields
    获取。硬编码列名会在遇到第一个非文本类型属性时引发bug。
  • 写入时必须遵循
    value_per_locale
    value_per_channel
    :不仅读取时要遵循,写入时也要。为按区域范围划分的属性只写入一行数据会丢失其他区域的值。
  • 单选/多选值是选项ID:而非标签。单选框(select)在
    integer_value
    中存储一个ID;多选框(multiselect)在
    text_value
    中存储逗号分隔的ID。
  • 选项标签支持翻译:标签存储在
    attribute_option_translations
    表中,因此在一个区域添加的标签必须在所有22个区域中添加。
  • 修改属性
    type
    会导致现有值失效
    :因为新类型会读取不同的列。应将其视为数据迁移,而非简单编辑。
  • 修改影响列表的内容后需重新索引:可筛选和列表属性会被扁平索引器反规范化到
    product_flat
    表中;索引完成前,网格和店铺前台会显示旧值。
必备子技能:在确认任何修改完成前,使用bagisto-change-verification工具。