前端表单能看到“门禁说明”,Network里的shippingAddress.extension_attributes也有值,保存订单后 sales_order_address 却是空的。这个问题通常不是一个插件就能解决,因为字段要穿过四层:UI数据、Web API数据对象、Quote Address持久化、Quote转Order。任何一层没声明或没复制,后面都会消失。
1. 先确认字段放在正确的payload位置
不同字段集成方式可能使用customAttributes或extension_attributes。打开shipping-information请求,确认最终JSON结构与后端接口的数据对象一致,不要只看Knockout组件里的值。
shipping_address: {
...
extension_attributes: {
access_note: "Call on arrival"
}
}
2. 声明接口扩展属性
<extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
<attribute code="access_note" type="string" />
</extension_attributes>
<extension_attributes for="Magento\Sales\Api\Data\OrderAddressInterface">
<attribute code="access_note" type="string" />
</extension_attributes>
修改extension_attributes.xml后清生成代码并重新DI编译,检查生成接口里确实出现getAccessNote/setAccessNote。只清配置缓存不足以更新生成类。
3. 把值落到Quote Address
在ShippingInformationManagement保存前后选择合适插件,将extension attribute复制到Quote Address的数据字段。数据库需要声明quote_address.access_note列。插件要处理extensionAttributes为null的情况,也要做长度与字符校验。
$ext = $address->getExtensionAttributes();
if ($ext && $ext->getAccessNote() !== null) {
$address->setData("access_note", trim($ext->getAccessNote()));
}
4. Quote转Order时复制字段
在etc/fieldset.xml配置shipping_address_convert_quote_address到order_address,或使用转换事件插件。
<fieldset id="sales_convert_quote_address">
<field name="access_note">
<aspect name="to_order_address" />
</field>
</fieldset>
数据库同时要有sales_order_address.access_note。若字段也要通过订单REST/GraphQL返回,OrderAddressInterface的extension attribute还需要加载插件,把数据库值放回扩展对象。
常见漏点
- 只给shipping地址加了字段,billing地址流程没有处理
- 游客接口正常,登录客户接口走了不同Service
- 多地址结账使用了另一套转换流程
- 插件sortOrder导致值在后续处理器中被覆盖为空
- 前端把字段放进custom_attributes,后端却只读extension_attributes
端到端测试
- 游客下单与登录下单
- 配送地址和账单地址不同
- 门禁说明为空、超长、含中文和特殊字符
- 后台查看订单、REST读取订单和邮件模板显示一致
- 重复提交订单时字段不会串到另一张Quote
不要只在下单后Observer里从Request硬取值。异步下单、API下单或重试时未必还有原始HTTP请求,字段应跟随Quote数据模型流转。

