如何移除 Sylius checkout 的 Adressing 和 Shipping 步骤
结论
这个报错与 checkout_resolver 无关。直接原因是路由配置引用了一个不存在的控制器方法:
_controller: sylius.controller.order:completeAction
Sylius 的 checkout 完成页仍由通用的资源更新动作处理,应改为:
_controller: sylius.controller.order:updateAction
结账状态迁移则由 _sylius.state_machine.transition: complete 执行。complete 页面还应使用适合订单确认阶段的 repository 方法,例如当前版本提供的 findCartForSummary。不要将购物车对象直接传给通常接收 ID 的 find()。
正确的路由配置
以下配置适用于使用 winzou_state_machine、sylius.controller.order:updateAction 和旧式 Bundle 模板路径的 Sylius 版本。
# app/Resources/SyliusShopBundle/config/routing/checkout.yml
sylius_shop_checkout_start:
path: /
methods: [GET]
defaults:
_controller: FrameworkBundle:Redirect:redirect
route: sylius_shop_checkout_select_payment
sylius_shop_checkout_select_payment:
path: /select-payment
methods: [GET, PUT]
defaults:
_controller: sylius.controller.order:updateAction
_sylius:
event: payment
flash: false
template: SyliusShopBundle:Checkout:selectPayment.html.twig
repository:
method: findCartForSelectingPayment
arguments:
- "expr:service('sylius.context.cart').getCart().getId()"
state_machine:
graph: sylius_order_checkout
transition: select_payment
redirect:
route: sylius_shop_checkout_complete
form:
type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\SelectPaymentType
sylius_shop_checkout_complete:
path: /complete
methods: [GET, PUT]
defaults:
_controller: sylius.controller.order:updateAction
_sylius:
event: complete
flash: false
template: SyliusShopBundle:Checkout:complete.html.twig
repository:
method: findCartForSummary
arguments:
- "expr:service('sylius.context.cart').getCart().getId()"
state_machine:
graph: sylius_order_checkout
transition: complete
redirect:
route: sylius_shop_order_pay
parameters:
tokenValue: resource.tokenValue
form:
type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\CompleteType
options:
validation_groups: sylius_checkout_complete
其中最重要的是修改控制器:
_controller: sylius.controller.order:updateAction
同时调整 repository 配置:
repository:
method: findCartForSummary
arguments:
- "expr:service('sylius.context.cart').getCart().getId()"
如果当前 Sylius 版本没有 findCartForSummary(),需要检查该版本的 OrderRepository,选择能够加载当前购物车及其支付、配送等关联数据的方法。不同 Sylius 版本的 repository API 并不完全相同。
checkout_resolver 配置
YAML 缩进必须正确,checkout_resolver 应位于 sylius_shop 下:
# app/config/config.yml
sylius_shop:
checkout_resolver:
pattern: /checkout/.+
route_map:
cart:
route: sylius_shop_checkout_select_payment
payment_selected:
route: sylius_shop_checkout_complete
payment_skipped:
route: sylius_shop_checkout_complete
调整后的流程如下:
cart
-> sylius_shop_checkout_select_payment
-> select_payment transition
payment_selected
-> sylius_shop_checkout_complete
-> complete transition
completed
是否保留 payment_skipped,取决于项目是否允许零金额订单或无须支付的订单。如果订单可能进入 payment_skipped,就要将该状态映射到 complete 页面,否则 resolver 可能找不到下一步路由。
state machine 配置
删除 addressing 和 shipping 后,最小状态机可以保留以下状态和迁移:
# app/Resources/SyliusCoreBundle/config/app/state_machine/sylius_order_checkout.yml
winzou_state_machine:
sylius_order_checkout:
class: "%sylius.model.order.class%"
property_path: checkoutState
graph: sylius_order_checkout
state_machine_class: "%sylius.state_machine.class%"
states:
cart: ~
payment_selected: ~
payment_skipped: ~
completed: ~
transitions:
select_payment:
from: [cart, payment_selected]
to: payment_selected
complete:
from: [payment_selected, payment_skipped]
to: completed
callbacks:
after:
sylius_process_cart:
on: ["select_payment"]
do: ["@sylius.order_processing.order_processor", "process"]
args: ["object"]
sylius_create_order:
on: ["complete"]
do: ["@sm.callback.cascade_transition", "apply"]
args: ["object", "event", "'create'", "'sylius_order'"]
sylius_save_checkout_completion_date:
on: ["complete"]
do: ["object", "completeCheckout"]
最好以项目当前安装版本自带的 sylius_order_checkout 配置为基础,只调整 states、transitions 和必要的回调触发条件。各版本的回调名称和参数可能不同,直接复制其他版本的整份状态机配置,反而容易产生新的错误。
还要检查 completeCheckout() 的方法签名。如果该方法不接收参数,就不能配置:
args: ["object"]
否则即使控制器问题已经修复,完成订单时仍可能出现参数数量错误。
为什么必须使用 updateAction
OrderController 是资源控制器,checkout 的完成操作并不是由名为 completeAction() 的专用方法处理。实际流程是:
updateAction加载当前购物车;- 创建并处理
CompleteType表单; - 表单有效后执行
complete状态迁移; - 触发状态机中的订单创建、完成时间等回调;
- 根据
_sylius.redirect跳转到支付页或后续页面。
所以,complete 是状态机 transition 的名称,不是控制器 action 的名称。配置成 completeAction 后,Symfony 会尝试调用一个不存在的方法,因此报错:
Expected method "completeAction" on class
"Sylius\Bundle\CoreBundle\Controller\OrderController"
表单导航也要提交正确的方法
select-payment 页面上的“下一步”按钮应提交当前表单,由 PUT /select-payment 执行 select_payment transition,不能用普通链接直接打开 /complete。
示意写法如下:
<button type="submit" class="ui large primary icon labeled button">
<i class="arrow right icon"></i>
{{ 'sylius.ui.next'|trans }}
</button>
如果用户直接访问 /checkout/complete,但订单状态仍是 cart,resolver 应将用户送回 select-payment。只有支付方式成功提交,订单状态变为 payment_selected 后,complete 页面才是合法的下一步。
移除步骤不等于移除业务约束
修改路由和状态机只能跳过页面,并不会自动补齐缺少的地址和配送数据。还需要根据业务类型确认:
- 订单是否包含需要配送的实体商品;
- shipment 是否仍会由订单处理器创建;
- 是否必须填写 billing/shipping address;
- 支付方式规则是否依赖配送渠道或地址;
sylius_checkout_complete验证组是否要求地址或配送方式。
如果销售虚拟商品,可以在创建购物车或处理订单时预先设置所需数据,也可以根据这种订单类型调整验证和处理逻辑。如果销售实体商品,通常不能只删除页面,还要提供默认地址、默认配送方式,或者实现自动选择配送方式的订单处理器。
最后要保证三部分互相匹配:resolver 引用的状态必须存在于 state machine 中,路由声明的 transition 必须能从当前状态执行,控制器则统一使用实际可调用的 updateAction。
备注:内容仅供参考。
