Symfony Workflow API 参考:WorkflowInterface 全部方法清单与示例指南
【免费下载链接】workflowProvides tools for managing a workflow or finite state machine项目地址: https://gitcode.com/gh_mirrors/workflo/workflow
🧭 Symfony Workflow 组件是什么?
Symfony Workflow(位于workflo/workflow)是 Symfony 全家桶中的工作流组件,提供了一套管理**工作流(Workflow)或有穷状态机(Finite State Machine)**的工具。你可以用它来管理订单审批、文章发布、任务流转等典型的"状态推进"场景:定义状态(places)、状态之间的迁移(transitions),然后对业务对象安全地执行状态变更。
本指南带你完整梳理 WorkflowInterface.php 中定义的9 个方法——这是与 Workflow 交互的核心 API,读懂它就等于读懂了整个组件的使用方式。
🏗️ 先搭一个最小可用的工作流
在逐方法讲解前,先用几行代码搭一个"博客文章"工作流,后文所有示例都基于它。你可以用 DefinitionBuilder.php 来声明状态与迁移:
use Symfony\Component\Workflow\DefinitionBuilder; use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore; use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Workflow; $builder = (new DefinitionBuilder()) ->addPlaces(['draft', 'under_review', 'published', 'rejected']) ->setInitialPlaces('draft') ->addTransition(new Transition('publish', 'under_review', 'published')) ->addTransition(new Transition('reject', 'under_review', 'rejected')) ->addTransition(new Transition('revise', 'under_review', 'draft')) ->addTransition(new Transition('submit', 'draft', 'under_review')); $workflow = new Workflow($builder->build(), new MethodMarkingStore(), null, 'post');其中 Transition.php 描述一次迁移:名字 + 从哪些状态出发(froms)+ 到达哪些状态(tos)。StateMachine.php 则提供了"一次只能处于一个状态"的状态机变体,后面会单独说明。
📋 WorkflowInterface 全部 9 个方法速查表
| # | 方法 | 作用一句话 | 返回值 |
|---|---|---|---|
| 1 | getMarking($subject) | 获取对象当前所处状态(标记) | Marking |
| 2 | can($subject, $transitionName) | 判断某个迁移现在能否执行 | bool |
| 3 | buildTransitionBlockerList($subject, $transitionName) | 查明迁移被阻塞的原因 | TransitionBlockerList |
| 4 | apply($subject, $transitionName, $context = []) | 触发迁移,真正把状态推进 | Marking |
| 5 | getEnabledTransitions($subject) | 列出对象当前所有可执行的迁移 | Transition[] |
| 6 | getEnabledTransition($subject, $name) | 按名称取回某个已启用的迁移 | ?Transition |
| 7 | getName() | 获取工作流名称 | string |
| 8 | getDefinition() | 获取状态与迁移的完整定义 | Definition |
| 9 | getMarkingStore() | 获取状态的存储策略 | MarkingStoreInterface |
💡 补充说明:接口中
getMetadataStore()用于获取元数据存储(见 MetadataStoreInterface.php),通常配合Definition一起使用,方便查询状态上的标签、说明等附加信息。
🔍 核心方法逐个详解
1.getMarking()—— 读取对象当前状态
getMarking()返回一个 Marking 对象,它记录了"令牌(token)在哪些状态上"。首次调用时若对象还没有标记,Workflow 会自动把令牌放到初始状态上:
$marking = $workflow->getMarking($post); $marking->getPlaces(); // 例如 ['draft' => 1] $marking->has('draft'); // true在 Workflow(工作流网)模型中,一个对象可以同时处于多个状态,因此返回的是"状态名 => 令牌数"的数组;这正是它与状态机的关键区别。若标记中的状态名在定义里不存在,会抛出LogicException(定义见 LogicException.php)。
2.can()—— 执行前先"试探"一下
if ($workflow->can($post, 'publish')) { // 迁移可用,可以放心 apply }can()会综合检查两件事:当前标记是否满足迁移的"出发状态"要求,以及守卫(Guard)监听器是否放行(守卫机制见 GuardListener.php)。它只判断、不执行,是写前端按钮"是否可点击"时的最佳帮手。
3.buildTransitionBlockerList()—— 迁移为什么被挡住了?
can()只告诉你"能不能",buildTransitionBlockerList()则回答"为什么不能"。它返回一个 TransitionBlockerList,里面记录了每一类阻塞原因:
$blockers = $workflow->buildTransitionBlockerList($post, 'publish'); foreach ($blockers as $blocker) { // $blocker->getReason() 例如 'blocked_by_marking'、'blocked_by_guard' }常见原因有两类:
- BLOCKED_BY_MARKING:对象不在迁移要求的出发状态上;
- BLOCKED_BY_GUARD:守卫监听器显式拦截(比如缺少某个字段)。
若迁移名称根本没定义,会抛出 UndefinedTransitionException。
4.apply()—— 触发迁移,真正推进状态
这是整个组件里最重要的方法。调用它会按固定顺序执行并派发事件:离开旧状态(leave)→ 迁移中(transition)→ 进入新状态(enter)→ 全部完成(entered / completed)→ 广播可用迁移(announce),最终返回新的Marking:
try { $workflow->apply($post, 'publish', ['editor' => 'admin']); } catch (NotEnabledTransitionException $e) { // 迁移当前不可用 } catch (UndefinedTransitionException $e) { // 迁移不存在 }对应异常类:NotEnabledTransitionException.php、UndefinedTransitionException.php。第三个参数$context是上下文数组,会随事件传递给各监听器,例如把"是谁执行的操作"带进去做审计记录(审计示例见 AuditTrailListener.php)。
5.getEnabledTransitions()/ 6.getEnabledTransition()—— 拿到"可操作菜单"
getEnabledTransitions($post)返回当前所有可执行的Transition[],非常适合渲染后台操作按钮列表;getEnabledTransition($post, 'publish')按名称取回单个迁移,若不可用则返回null。
两者都基于"标记满足 + 守卫放行"的规则计算,与can()的判定逻辑一致,区别只在于返回的是完整的迁移对象,你能进一步调用getName()、getFroms()、getTos()。
7~9. 名称、定义与存储:getName()、getDefinition()、getMarkingStore()
getName():返回构建时指定的名称(如'post')。当同一对象被多个工作流管理时(比如"内容流"+"安全审核流"),靠名称区分,这也是 Registry.php 按名查找工作流的依据;getDefinition():返回 Definition.php 对象,包含全部状态、迁移、初始状态和元数据,调试时打印它最能定位配置问题;getMarkingStore():返回 MarkingStoreInterface.php 的实现,决定"标记存在哪里"。内置的 MethodMarkingStore.php 通过对象的getMarking()/setMarking()方法存取;在 Symfony 框架中还可以换成属性标记存储,直接落在实体的某个字段上(测试示例见 PropertiesMarkingStoreTest.php)。
⚙️ Workflow 与 StateMachine 有什么区别?
两者共用同一套接口,差异只在语义上:
| 对比项 | Workflow | StateMachine |
|---|---|---|
| 同时状态数 | 多个(工作流网) | 严格一个 |
apply前检查 | 不强制唯一状态 | 强制当前只有 1 个状态,否则报错 |
| 典型场景 | 复杂流程编排(多人协作节点) | 简单对象生命周期 |
| 实现位置 | Workflow.php | StateMachine.php |
StateMachine.php 继承自Workflow,只是构造时默认使用"单状态模式"的MethodMarkingStore。如果你的业务是"订单:待付款 → 已付款 → 已发货 → 已完成"这种线性流转,用StateMachine更直观。
⚡ 附赠:与 9 个方法配套的 7 种事件
apply()之所以强大,是因为它会派发一系列事件,全部常量定义在 WorkflowEvents.php:
| 事件常量 | 事件名 | 派发时机 |
|---|---|---|
GUARD | workflow.guard | 判断迁移是否放行时 |
LEAVE | workflow.leave | 离开旧状态后 |
TRANSITION | workflow.transition | 迁移进行中 |
ENTER | workflow.enter | 进入新状态后 |
ENTERED | workflow.entered | 迁移收尾后 |
COMPLETED | workflow.completed | 迁移完成时 |
ANNOUNCE | workflow.announce | 广播后续可用迁移 |
在 Workflow.php 中可以看到每个事件还会派生形如workflow.post.enter.published的精细命名,方便你只监听"某工作流 + 某状态"的变化。事件类源码集中在 Event/ 目录,例如 EnterEvent.php、GuardEvent.php。
🖼️ 把工作流画出来:workflow:dump 命令
配置好后可运行workflow:dump命令导出图形描述,支持PlantUML、Mermaid、DOT三种格式(命令源码见 WorkflowDumpCommand.php,渲染器分别位于 PlantUmlDumper.php、MermaidDumper.php、GraphvizDumper.php):
bin/console workflow:dump post --dump-format=mermaid bin/console workflow:dump post --dump-format=puml配合--with-metadata可把元数据画进图里,--with-listeners可把监听器标注在对应状态节点上。输出的参考样例可查看 Tests/Fixtures/puml/ 下的.puml文件,直观感受状态图最终长什么样。
📂 核心源码文件导航
| 模块 | 文件 | 说明 |
|---|---|---|
| 核心接口 | WorkflowInterface.php | 本文主角,9 个方法定义处 |
| 默认实现 | Workflow.php | apply、事件派发逻辑 |
| 状态机 | StateMachine.php | 单状态约束变体 |
| 状态定义 | Definition.php、DefinitionBuilder.php | 状态与迁移的声明 |
| 迁移对象 | Transition.php、Arc.php | from/to 弧与权重 |
| 标记存储 | MarkingStore/ | 标记的读写策略 |
| 守卫机制 | EventListener/GuardListener.php | 表达式守卫 |
| 图形导出 | Dumper/ | puml / mermaid / dot |
| 单元测试 | Tests/WorkflowTest.php | 最完整的 API 使用示例 |
🎯 总结
- 查状态用
getMarking();试状态用can();问原因用buildTransitionBlockerList();推状态用apply(); getEnabledTransitions()是渲染操作入口的首选方法;- 简单生命周期选
StateMachine,复杂协作流程选Workflow; - 想深入源码,从 Tests/WorkflowTest.php 读起,它几乎覆盖了
WorkflowInterface的每一种用法与边界情况。
【免费下载链接】workflowProvides tools for managing a workflow or finite state machine项目地址: https://gitcode.com/gh_mirrors/workflo/workflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考