Пошаговая Форма В Symfony2 С Syliusflowbundle

Иногда при разработке веб-приложений, отличных от примитивного блога или сайта-визитки, можно столкнуться с необходимостью создания громоздких форм — информации о пользователе с несколькими адресами, личными данными, кличками домашних животных и т. д. Создание пошаговой формы может облегчить жизнь пользователям, но не всегда разработчикам.

В попытке помочь последнему и был написан этот пост. Мы рассмотрим уже упомянутый комплект в платформе электронной коммерции Хабра.

Силиус SyliusFlowBundle .

Почему он? Рекомендация коллеги, которого мучила негибкость CraueFormFlowBundle, проектная документация и симпатичный дизайн sylius.org склонили внутренние чаши весов в сторону описанного решения.

В общем, поехали.

Сначала нужно подтянуть сам пучок:

  
  
  
  
  
  
   

composer require "sylius/flow-bundle"

Далее редактируем AppKernel.php:

<Эphp // app/AppKernel.php public function registerBundles() { $bundles = array( new Sylius\Bundle\FlowBundle\SyliusFlowBundle(), // Other bundles. ); }

Чтобы наша форма запустилась, нам нужно сделать следующее:
  1. Создайте необходимые шаги — объекты, реализующие интерфейс StepInterface, с шаблоном для каждого из них.

  2. Свяжите шаги в скрипт — интерфейс ProcessScenarioInterface и объявите его как сервис.

  3. Импорт маршрутизации из пакета
Это все, что вам нужно для получения работающей пошаговой формы.

Теперь рассмотрим каждый из этапов более подробно.



Создание шагов

Как упоминалось выше, SyliusFlowBundle требует от нас реализации StepInterface; для этого удобно использовать любезно предоставленную нам абстракцию ControllerStep, которая, как следует из названия, в свою очередь наследует контроллер Symphony. Решение предполагает разбить шаг на два действия — displayAction и frontAction. Первый явно отображает форму, второй обрабатывает POST-запрос и перенаправляет пользователя на следующий шаг.

Скорее всего, ваши шаги будут иметь много общего кода, который можно будет поместить в отдельный абстрактный класс.

Например вот так: Базовый шаг

abstract class BaseStep extends ControllerStep { const USER = 'user'; /** {@inheritdoc} */ public function displayAction(ProcessContextInterface $context) { return $this->createView( $this->getStepForm($context->getStorage()->get(self::USER)), $context ); } /** {@inheritdoc} */ public function forwardAction(ProcessContextInterface $context) { $form = $this->getStepForm($context->getStorage()->get(self::USER)); $form->handleRequest($context->getRequest()); if ($context->getRequest()->isMethod('POST') && $form->isValid()) { return $this->onFormValid($form, $context); } return $this->createView($form, $context); } /** * @param Form $form * @param ProcessContextInterface $context * * @return Response */ abstract protected function createView(Form $form, ProcessContextInterface $context); /** * @param mixed $data * * @return Form */ abstract protected function getStepForm($data = null); /** * @param Form $form * @param ProcessContextInterface $context * * @return mixed */ abstract protected function onFormValid(Form $form, ProcessContextInterface $context); }

Здесь в displayAction мы извлекаем объект из хранилища (сессия по умолчанию), получаем форму в зависимости от конкретного шага и рендерим страницу (на конкретном шаге также нужно будет предоставить реализацию).

Единственная задача frontAction — проверка данных.

Наконец, давайте посмотрим на реализацию этого шага: Главный шаг

class MainStep extends BaseStep { /** {@inheritdoc} */ public function displayAction(ProcessContextInterface $context) { $context->getStorage()->remove(self::USER); return parent::displayAction($context); } /** {@inheritdoc} */ protected function createView(Form $form, ProcessContextInterface $context) { return $this->render('HospectAppBundle:Process:step.html.twig', [ 'form' => $form->createView(), 'context' => $context, ]); } /** {@inheritdoc} */ protected function getStepForm($data = null) { return $this->createForm(new MainInfoType(), $data); } /** {@inheritdoc} */ protected function onFormValid(Form $form, ProcessContextInterface $context) { $context->getStorage()->set(self::USER, $form->getData()); return $this->complete(); } }

Здесь предсказуемо создается требуемая форма, отображается представление для конкретного шага и, если данные действительны, сохраняются в сеансе.

Последний шаг должен, конечно же, сохранить многострадальный объект в ваше любимое постоянное хранилище данных.



Объединение шагов в скрипт

На этом этапе у нас от приложения отрезано несколько сферических ступенек.

Чтобы это изменить, вам нужно создать скрипт. Скрипт является связующим звеном общего вида и отвечает за такие вещи как: последовательность шагов, задание общих для всех шагов параметров маршрута, перенаправление после успешного завершения всех шагов и т.д. Однако на самом деле работы не так уж и много.

здесь — интерфейс требует реализации только одного метода — build, который принимает на вход только один параметр — builder с его обычными методами.

Пользовательский сценарий

class UserScenario extends ContainerAware implements ProcessScenarioInterface { /** {@inheritdoc} */ public function build(ProcessBuilderInterface $builder) { $builder ->add('main', new MainStep()) ->add('address', new AddressStep()) ->setRedirect('hospect_app_homepage'); } }

Чтобы система «узнала» о нашем сценарии, ее нужно объявить как сервис с тегом «sylius.process.scenario»:

sylius.scenario.flow: class: Hospect\AppBundle\Process\Scenario\UserScenario calls: - [ setContainer, [@service_container] ] tags: - { name: sylius.process.scenario, alias: user }



Конфигурация маршрутизации

Нам предложено всего три маршрута на всю форму, сколько бы ступенек в ней ни было.

У всех них есть обязательный параметр scriptAlias, на основании которого подтягивается нужный сценарий.

Маршруты «sylius_flow_display» и «sylius_flow_forward» требуют второго параметра с именем «stepName».

Вот и все.

Импорт выглядит следующим образом:

sylius_flow: resource: @SyliusFlowBundle/Resources/config/routing.yml prefix: /



Ссылки

Теги: #php #symfony 2 #sylius #php #symfony
Вместе с данным постом часто просматривают: