Иногда при разработке веб-приложений, отличных от примитивного блога или сайта-визитки, можно столкнуться с необходимостью создания громоздких форм — информации о пользователе с несколькими адресами, личными данными, кличками домашних животных и т. д. Создание пошаговой формы может облегчить жизнь пользователям, но не всегда разработчикам.
В попытке помочь последнему и был написан этот пост. Мы рассмотрим уже упомянутый комплект в платформе электронной коммерции Хабра.
Почему он? Рекомендация коллеги, которого мучила негибкость CraueFormFlowBundle, проектная документация и симпатичный дизайн sylius.org склонили внутренние чаши весов в сторону описанного решения.
В общем, поехали.
Сначала нужно подтянуть сам пучок:
Далее редактируем AppKernel.php:composer require "sylius/flow-bundle"
<Эphp
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Sylius\Bundle\FlowBundle\SyliusFlowBundle(),
// Other bundles.
);
}
Чтобы наша форма запустилась, нам нужно сделать следующее:
- Создайте необходимые шаги — объекты, реализующие интерфейс StepInterface, с шаблоном для каждого из них.
- Свяжите шаги в скрипт — интерфейс ProcessScenarioInterface и объявите его как сервис.
- Импорт маршрутизации из пакета
Теперь рассмотрим каждый из этапов более подробно.
Создание шагов
Как упоминалось выше, 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-
Важность Испытаний На Электробезопасность
19 Oct, 24 -
Как Сделать Ваш Сайт Быстрее
19 Oct, 24 -
Левитрон На Ардуино
19 Oct, 24 -
Опера В России: Отметка В 20% Пройдена
19 Oct, 24 -
Yahoo Задерживает Панаму
19 Oct, 24 -
Angularjs Против Knockoutjs
19 Oct, 24