- PVSM.RU - https://www.pvsm.ru -
Формы, возможно, один из самых сложных компонентов Symfony2. Но за всей его сложностью скрывается поразительно гибкая архитектура, предоставляющая широкие возможности для расширения. Мы, как разработчики, можем добавлять (и изменять) типы полей форм (Form Type), использовать слушатели (Event Listeners), преобразователи данных (Data Transformers) и расширения типов (Type Extensions). О последних сегодня и поговорим.
Расширения типов предоставляют мощный механизм для изменения поведения и представления (FormView) типов полей. В пределах расширения предоставляется 4 точки входа для реализации необходимой логики:
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
}
public function finishView(FormView $view, FormInterface $form, array $options)
{
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
}
Рассмотрим их подробнее:
В большинстве случаев мы будем оперировать методами setDefaultOptions и buildView. Один из распространенных алгоритмов применения расширений форм:
Контролировать тип полей для которого применяется расширение нам позволяет метод getExtendedType:
public function getExtendedType()
{
// расширение будет применено для всех полей типа textarea
return 'textarea';
}
Для использования расширения в Symfony 2 Framework его необходимо зарегистрировать как сервис в контейнере зависимостей (Dependency Injection Container) с помощью специального тэга — form.type_extension. Для тэга необходимо указать параметр alias с указанием типа полей к которому будет применяться расширение:
services:
acme_demo.form.my_extension:
class: AcmeDemoBundleFormExtensionMyExtension
tags:
- { name: form.type_extension, alias: textarea }
Для примера реализуем расширение позволяющее группировать поля формы и выводить их внутри элемента <fieldset>.
<?php
namespace AcmeDemoBundleFormExtension;
use SymfonyComponentFormAbstractTypeExtension;
use SymfonyComponentFormFormView;
use SymfonyComponentFormFormInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
use SymfonyComponentOptionsResolverOptions;
class FieldsetExtension extends AbstractTypeExtension
{
private $rootView;
public function getExtendedType()
{
// расширение будет работать с любым типом полей
return 'form';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
// По умолчанию группировка не происходит.
'group' => null,
));
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$group = $options['group'];
if (null === $group) {
return;
}
$root = $this->getRootView($view);
$root->vars['groups'][$group][] = $form->getName();
}
public function getRootView(FormView $view)
{
$root = $view->parent;
while (null === $root) {
$root = $root->parent;
}
return $root;
}
}
Создадим шаблон форм:
{# src Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{% block form_widget_compound %}
<div {{ block('widget_container_attributes') }}>
{% if form.parent is empty %}
{{ form_errors(form) }}
{% endif %}
{% if form.vars.groups is defined %}
{% for group,items in form.vars.groups %}
<fieldset>
<legend>{{ group|title|trans({}, translation_domain) }}</legend>
{% for item in items %}
{{ form_row(form[item]) }}
{% endfor %}
</fieldset>
{% endfor %}
{% endif %}
{{ form_rest(form) }}
</div>
{% endblock form_widget_compound %}
После регистрации расширения в контейнере зависимостей, можно приступать к использованию. Создадим новую форму:
<?php
namespace AcmeDemoBundleFormExtension;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array(
'group' => 'fio'
))
->add('surname', 'text', array(
'group' => 'fio'
))
->add('midname', 'text', array(
'group' => 'fio'
))
->add('phone', 'text', array(
'group' => 'contacts'
))
->add('skype', 'text', array(
'group' => 'contacts'
))
->add('email', 'text', array(
'group' => 'contacts'
))
;
}
}
И шаблон для неё:
{# src Acme/DemoBundle/Resources/views/Person/new.html.twig #}
{% form_theme form 'AcmeDemoBundle:Form:fields.html.twig' %}
<form action="{{ path('person_create') }}" >
{{ form_widget(form) }}
</form>
Итак расширение работает, но реализация не достаточно удобна, на мой взгляд. Попробуем упростить и добавить немного синтаксического сахара. Для этого создадим класс-обертку:
<?php
namespace AcmeDemoBundleForm;
use SymfonyComponentFormFormBuilder;
class FormMapper
{
/**
* Form builder
* @var FormBuidler
*/
private $builder;
/**
* Active group
* @var mixed null|string
*/
private $group = null;
public function __construct(FormBuilder $builder)
{
$this->builder = $builder;
}
/**
* Add child to builder with group option
*/
public function add($child, $type = null, array $options = array())
{
if (!array_key_exists('group', $options) and null !== $this->group) {
$options['group'] = $this->group;
}
$this->builder->add($child, $type, $options);
return $this;
}
/**
* Set active group
*/
public function with($group)
{
$this->group = $group;
return $this;
}
}
Теперь управление группами стало проще:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$mapper = new FormMapper($builder);
$mapper
->with('fio')
->add('name', 'text')
->add('surname', 'text')
->add('midname', 'text')
->with('contacts')
->add('phone', 'text')
->add('skype', 'text')
->add('email', 'text')
;
}
При подготовки статьи использовались материалы:
Автор: zloyusr
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/37267
Ссылки в тексте:
[1] Документация Symfony 2 Form Component: http://symfony.com/doc/master/book/forms.html
[2] Рецепты Symfony 2 Forms: http://symfony.com/doc/master/cookbook/form/index.html
[3] Источник: http://habrahabr.ru/post/184484/
Нажмите здесь для печати.