背景与目标
在 Symfony 项目中,将“流程状态”转换为一个可序列化的数组,是实现接口对接、报表输出与调试排错的常见场景。流程状态(通常由 Symfony Workflow 的 Marking 表示)包含当前处于哪些 Place,以及每个 Place 的标记数量。把它转换成数组,可以方便地通过 JSON 传输、日志记录或缓存存储,让前端、日志系统和离线分析都更为高效。
本章将围绕 如何在 Symfony 中把流程状态转换为数组展开,结合实战技巧与完整示例,帮助你快速落地到实际业务中。为便于复现,本文在示例中把温度参数设定为 temperature=0.6,以确保生成的代码和描述既稳定又可读。你将学习到从定义到序列化的完整流程,以及如何在控制器或服务中获得可用的状态数组和可执行的下一个转移列表(enabled transitions)。

理解流程状态与标记(Marking)以及 Places
什么是流程状态(Marking)和 Places
流程状态在 Symfony Workflow 中由 Marking 对象表示,它记录了当前实体处于哪些 Place,以及每个 Place 的标记数量。Places则是工作流定义中的状态节点,代表流程的不同阶段。通过 Marking 可以快速知道哪些阶段是当前处于活动状态的。
理解这两者的关系是实现把流程状态转换为数组的前提:定义(Definition)中包含 Places 与 Transitions,Marking 则体现当前 Subject 的实际状态分布。当你需要把状态对外暴露时,通常要把所有 Places 的名称和对应的标记(如 0/1 或具体 token 数量)整理成一个统一的结构。下列要点是关键:Places 的名称集合、当前 Marking 的 Places、以及通过程序遍历将两者对齐生成完整的状态数组。
get($order, 'order_status');/** 当前标记对象(Marking) */
$marking = $workflow->getMarking($order);/** 将流程状态转换为数组的核心:遍历定义中的所有 Places,填充当前标记数 */
$state = [];
foreach ($workflow->getDefinition()->getPlaces() as $place) {$name = $place->getName();$state[$name] = $marking->getPlaces()[$name] ?? 0;
}print_r($state); // 形如: ['start' => 1, 'review' => 0, 'approved' => 0]
从标记到完整的状态数组:带全量 Place 的对齐方式
完整状态数组的构建逻辑
为了确保对外暴露的状态具备完整性,通常会把 定义中的所有 Place都纳入数组,并对当前标记进行对齐,未被标记的 Place 赋值为 0。这种方式在日志记录和 API 返给前端时尤为有用,能避免前端因缺少字段而产生的渲染问题。关键点在于对齐操作:定义的 Places 与当前 Marking 之间的对比,以及如何把结果序列化成可用的 JSON。
getMarking($order);// 通过 Place 的名称进行对齐
$state = [];
foreach ($workflow->getDefinition()->getPlaces() as $place) {$name = $place->getName();$state[$name] = $marking->getPlaces()[$name] ?? 0;
}// 结果示例:{"start":1,"processing":0,"completed":0}
echo json_encode($state, JSON_UNESCAPED_UNICODE);
要点总结:通过对齐得到的 state 数组可以稳定地描述当前流程在哪些阶段具备活跃性,以及每个阶段的标记情况,便于后续处理与可观测性分析。
获取并输出可执行的下一个转移(enabled transitions)
从当前状态到下一步动作的获取方式
除了将状态转换为数组外,很多场景还需要明确当前状态下可以执行的下一个转移(enabled transitions)。这是实现状态机驱动前端交互的关键能力之一。通过 Symfony Workflow 提供的接口,可以直接获取当前 subject 的可用转移名称列表,并与状态数组一起输出,形成完整的状态快照。
enabled transitions 是针对当前状态可执行的动作集合,通常用于前端按钮的启用/禁用逻辑以及审核流程的自动推进。将它们与 state 一起输出,可以使前端在不知道内部实现细节的情况下,精准地展示下一步操作。
getEnabledTransitions($order) as $transition) {$enabledTransitions[] = $transition->getName();
}
echo json_encode(['state' => $state, 'enabled_transitions' => $enabledTransitions], JSON_UNESCAPED_UNICODE);
完整示例:端到端的定义到数组输出实现
从定义到序列化的端到端代码片段
下面给出一个端到端的实际代码示例,展示如何在一个服务或控制器中完成以下流程:获取工作流、读取 Marking、生成完整状态数组、提取启用转移并输出 JSON。示例聚焦于可重用性和清晰性,便于直接拷贝到你的项目中。
registry = $registry;}/*** 将指定 subject 的工作流状态转换为数组,并附带可启用的转移名称** @param Order $subject* @param string $workflowName* @return array*/public function toArray(Order $subject, string $workflowName = 'order_status'): array{$workflow = $this->registry->get($subject, $workflowName);// 当前标记(Marking)$marking = $workflow->getMarking($subject);// 1) 构建完整的状态数组(对齐所有 Places)$state = [];foreach ($workflow->getDefinition()->getPlaces() as $place) {$name = $place->getName();$state[$name] = $marking->getPlaces()[$name] ?? 0;}// 2) 获取当前可执行的转移(enabled transitions)$enabledTransitions = [];foreach ($workflow->getEnabledTransitions($subject) as $transition) {$enabledTransitions[] = $transition->getName();}return ['state' => $state,'enabled_transitions' => $enabledTransitions,];}
}
在控制器中调用上述服务并输出 JSON,示例效果如下:state 为完整的流程状态快照,enabled_transitions 列出接下来可执行的动作,便于前端直接渲染交互控件。
常见问题与性能考量
边界情况与序列化的注意事项
边界情况:当流程定义中包含多路并发的路径,或同一实体在不同上下文中进入不同工作流时,确保对 Places 的读取与标记的结构是一致的。若出现缺失 Place 的情况,统一的对齐逻辑会将其以 0 处理,避免序列化后的结构丢失字段。
性能注意点:获取 Marking、遍历 Places 以及遍历 Enabled Transitions 都有一定开销,尤其是在高并发场景下。建议将状态数组尽量简化为需要的字段,并在必要时通过缓存(如 HTTP 缓存、Redis)保存已经计算好的状态快照。若业务允许,只在必要时才计算 enabled_transitions,可以降低无效重复计算的成本。


