Symfony 2フォームの拡張

前戯



フォームは間違いなくSymfony2の最も複雑なコンポーネントの1つです。 しかし、その複雑さの背後には、拡張のための十分な余地を提供する驚くほど柔軟なアーキテクチャがあります。 開発者として、フォームフィールドタイプを追加(および変更)し、イベントリスナー、データトランスフォーマー、およびタイプ拡張を使用できます。 今日は後者についてお話します。



理論



型拡張は、フィールド型の動作と表示(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つ:

  1. setDefaultOptions - OptionsResolverに新しいオプションを登録します。
  2. buildView-オプション値を処理するロジックを実装し、新しいパラメーターをビューに渡します。
  3. フォームテンプレートを変更して、パラメーター値を表示します。


getExtendedTypeメソッドを使用すると、拡張機能が適用されるフィールドのタイプを制御できます。

 public function getExtendedType() { //        textarea return 'textarea'; }
      
      





Symfony 2フレームワークで拡張機能を使用するには、特別なタグform.type_extensionを使用してDependency Injection Containerにサービスとして登録する必要があります。 タグには、拡張機能が適用されるフィールドのタイプを示すエイリアスパラメーターを指定する必要があります。

 services: acme_demo.form.my_extension: class: Acme\DemoBundle\Form\Extension\MyExtension tags: - { name: form.type_extension, alias: textarea }
      
      





練習する



たとえば、フォームフィールドをグループ化し、<fieldset>要素内に表示できる拡張機能を実装します。

 <?php namespace Acme\DemoBundle\Form\Extension; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\OptionsResolver\Options; 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 Acme\DemoBundle\Form\Extension; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; 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 Acme\DemoBundle\Form; use Symfony\Component\Form\FormBuilder; 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フォームコンポーネントドキュメント
  2. Symfony 2フォームレシピ



All Articles