zikula / wizard
包含 Symfony 表单的多阶段交互向导
Requires
- php: >=7.2.0
- symfony/config: 4.*|5.*
- symfony/dependency-injection: 4.*|5.*
- symfony/filesystem: 4.*|5.*
- symfony/form: 4.*|5.*
- symfony/http-foundation: 4.*|5.*
- symfony/yaml: 4.*|5.*
README
向导已包含在核心仓库中。
向导
向导组件是多阶段用户交互的管理工具。它利用多个接口和向导类来创建一个与 Symfony 表单和 Twig 模板兼容的工作流程。依靠 阶段 的概念,开发者可以使用 .yml
文件定义一个序列,并在其控制器中控制该序列。
实例化时,向导类需要一个 StageContainer 和到 阶段定义文件(yaml 格式)的完整路径。向导将从那里加载阶段定义。向导组件为此包含一个 YamlFileLoader。
创建一个扩展 Zikula\Component\Wizard\AbstractStageContainer
的具体类。使用自动装配和自动配置来配置该类
_instanceof: # only works for classes that are configured in this file Zikula\Component\Wizard\StageInterface: tags: ['my_special_tag'] # if this is the only instance of the interface you will use, you can use an alias Zikula\Component\Wizard\StageContainerInterface: '@Acme\Bundle\MyCustomBundle\Container\FooStageContainer' Acme\Bundle\MyCustomBundle\Container\FooStageContainer: arguments: $stages: !tagged_iterator my_special_tag
阶段
阶段只是一个实现 StageInterface 的类。它定义了一个 名称、一个 模板名称 以及阶段将需要的任何 模板参数。阶段还必须定义它是否是 必需的,通过可能的逻辑完成并返回布尔值。标记为 NOT 必需的 的阶段将在其实例化和处理 isNecessary()
方法后跳过,允许该阶段在继续之前根据需要完成任务。当向导调用 getCurrentStage()
方法时,将跳过阶段。
使用 Symfony 自动装配和自动配置或手动依赖注入将服务添加到您的阶段。
阶段可以选择实现
InjectContainerInterface
如果阶段需要 Symfony 容器FormHandlerInterface
如果阶段将使用 Symfony 表单
向导可以在 isNecessary()
方法中通过抛出 AbortStageException
来停止。该消息可以通过 $wizard->getWarning()
获取。
阶段定义文件
阶段定义文件是一个简单的 yaml 文件。第一个键 stages:
是必需的,然后每个阶段都应该通过 name
列出,每个都应该具有 class
和 order
属性。name
键必须与 Stage 类中设置的阶段名称相同。class
属性应该是完全限定的类名(带有命名空间),而 order
属性应该是一个标识阶段顺序的整数。可选地,一个阶段可以用一个 default
属性来识别,应该设置为 true。如果未提供阶段参数,该阶段将由向导使用。
sample_stages.yml 示例
stages: prep: class: Acme\Bundle\DemoBundle\Stage\PrepStage order: 1 default: true getinfo: class: Acme\Bundle\DemoBundle\Stage\GetInfoStage order: 2 noform: class: Acme\Bundle\DemoBundle\Stage\NoFormStage order: 3 complete: class: Acme\Bundle\DemoBundle\Stage\CompleteStage order: 4 nonstage: class: Acme\Bundle\DemoBundle\Stage\NonStage order: 99
示例控制器
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouterInterface; use Zikula\Component\Wizard\FormHandlerInterface; use Zikula\Component\Wizard\StageContainerInterface; use Zikula\Component\Wizard\Wizard; use Zikula\Component\Wizard\WizardCompleteInterface; class MyController { /** * @var StageContainerInterface */ private $stageContainer; /** * @var \Twig\Environment */ private $twig; /** * @var FormFactoryInterface */ private $formFactory; /** * @var RouterInterface */ private $router; /** * define route = 'index/{stage}' */ public function indexAction(Request $request, $stage) { // begin the wizard $wizard = new Wizard($this->stageContainer, realpath(__DIR__ . '/../Resources/config/stages.yml')); $currentStage = $wizard->getCurrentStage($stage); if ($currentStage instanceof WizardCompleteInterface) { return $currentStage->getResponse($request); } $templateParams = $currentStage->getTemplateParams(); if ($wizard->isHalted()) { $request->getSession()->getFlashBag()->add('danger', $wizard->getWarning()); return new Response($this->twig->render('@MyCustomBundle/error.html.twig', $templateParams)); } // handle the form if ($currentStage instanceof FormHandlerInterface) { $form = $this->formFactory->create($currentStage->getFormType()); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $currentStage->handleFormResult($form); $url = $this->router->generate('index', ['stage' => $wizard->getNextStage()->getName()], true); return new RedirectResponse($url); } $templateParams['form'] = $form->createView(); } return new Response($this->twig->render($currentStage->getTemplateName(), $templateParams)); } }