Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Расширяем Symfony 2 Forms

Anna | 31.05.2014 | нет комментариев

Формы, допустимо, один из самых трудных компонентов 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)
{
}

Разглядим их подробнее:

  1. buildForm — предоставляет доступ к объекту FormBuilder, что разрешает добавить, изменить либо удалить поля формы, а также прикрепить слушателей;
  2. buildView — предоставляет доступ к объектам FormView и Form, для модификации представления формы. В пределах данного способа немыслимо изменить дочерние представления;
  3. finishView — схож с buildView, но разрешает изменять дочерние представления;
  4. setDefaultOptions — предоставляет доступ к объекту OptionsResolver, для растяжения либо метаморфозы списка опций.

В большинстве случаев мы будем оперировать способами setDefaultOptions и buildView. Один из распространенных алгорифмов использования растяжений форм:

  1. setDefaultOptions — регистрируем новую опцию в OptionsResolver;
  2. buildView — реализуем логику обработки значения опции и передаем новейший параметр в представление;
  3. изменяем образец формы для итога значения параметра.

Контролировать тип полей для которого используется растяжение нам разрешает способ 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')
    ;
}

 

Финал

При подготовки статьи применялись материалы:

  1. Документация Symfony 2 Form Component
  2. Рецепты Symfony 2 Forms

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB