一个自定义商品属性原本是 Store View 范围,后来改成 Global。后台默认视图显示正确,英文店铺却仍出现旧值;反向从 Global 改成 Store View 后,又发现所有店铺暂时相同。Scope 不是简单的界面开关,它决定 Magento 从哪些 store_id 行读取和回退。

先确认属性与值表

SELECT attribute_id, attribute_code, backend_type
FROM eav_attribute
WHERE attribute_code = 'custom_label';

SELECT is_global
FROM catalog_eav_attribute
WHERE attribute_id = 123;

is_global 的具体常量值应以代码定义为准,不要凭记忆直接 UPDATE。backend_type 决定值在 varchar、int、decimal、text 或 datetime 表。

Store View 覆盖如何残留

SELECT entity_id, store_id, value
FROM catalog_product_entity_varchar
WHERE attribute_id = 123
ORDER BY entity_id, store_id;

store_id=0 是默认值,其他行是覆盖。Scope 改为 Global 后,旧覆盖行可能仍在数据库,当前读取逻辑可能忽略它们,但以后再次改范围或某个导入脚本按 store 写入时会重新暴露。

Website Scope 不是直接用 website_id 存值

EAV 商品值仍通过 store_id 表达,网站级写入通常落在网站默认 store 的语义中,由 Magento 服务层处理。手工按 website_id 插入 EAV 行是错误做法。价格等特殊属性还有索引与计算逻辑,不能照搬普通文本属性迁移。

先定义保留哪一个值

从 Store View 收敛到 Global 时,不同店铺可能有不同业务内容,必须由业务决定使用哪个作为默认值;不能简单选择最新一行。扩大到 Store View 时,通常保留 store_id=0 作为回退,只在确实不同的店铺建立覆盖。

通过服务层或数据补丁迁移

先导出 attribute_id、entity_id、SKU、store_id、旧值;在预发布生成转换计划;通过 Magento repository/import 或经过审计的数据补丁更新。变更属性定义后重建相关 EAV/搜索/价格索引并清理精确缓存。直接删除所有非零行不可逆,也可能丢掉翻译内容。

回归测试至少选择有覆盖、无覆盖、多个 Website 和可配置商品各一个。分别在默认、网站与 Store View 范围保存并读取,确认后台“Use Default Value”行为、前台展示和 API 返回都一致。

导入模板必须同步 Scope 设计

属性迁移正确后,旧 CSV/ERP 作业仍可能按 store_view_code 写入,从而重新制造覆盖。更新字段映射与导入说明,明确空值是“清除覆盖”还是“保持不变”。先用少量 SKU 验证导入,再检查 EAV 表的 store_id 分布,不能只看默认后台视图。