天天看點

Symfony2CookBook:如何建立自定義的表單域類型

  • 原文出處:http://symfony.com/doc/2.1/cookbook/form/create_custom_field_type.html
  • 原文作者:symfony.com
  • 授權許可:創作共用協定
  • 翻譯人員:FireHare
  • 校對人員:FireHare
  • 适用版本:Symfony 2.1
  • 文章狀态:草譯階段

Symfony comes with a bunch of core field types available for building forms. However there are situations where we want to create a custom form field type for a specific purpose. This recipe assumes we need a field definition that holds a person's gender, based on the existing choice field. This section explains how the field is defined, how we can customize its layout and finally, how we can register it for use in our application.

Symfony自帶了一組用于建構表單的核心表單域類型。但在有些情況下,我們為了某個目的需要建立自定義表單域類型。本文假定我們需要基于現有的choice表單域定義一個表單域,用來儲存使用者性别。本文将闡述如何定義表單域、如何設定它的布局及最終顯示、如何在應用程式中注冊以便使用。

Defining the Field Type

定義域類型

In order to create the custom field type, first we have to create the class representing the field. In our situation the class holding the field type will be called GenderType and the file will be stored in the default location for form fields, which is

<BundleName>\Form\Type

. Make sure the field extends

AbstractType

:

為了建立自定義域類型,首先我們需要建立該域對應的類。就我們這種情況而言,我們可以将保持域類型的類定義為GenderType,并将該檔案放置在表單域的預設位置:

<BundleName>\Form\Type 目錄中,并確定該表單域從

AbstractType

類擴充:

  1. // src/Acme/DemoBundle/Form/Type/GenderType.php 
  2. namespace Acme\DemoBundle\Form\Type; 
  3. use Symfony\Component\Form\AbstractType; 
  4. use Symfony\Component\OptionsResolver\OptionsResolverInterface; 
  5. class GenderType extends AbstractType 
  6.     public function setDefaultOptions(OptionsResolverInterface $resolver) 
  7.     { 
  8.         $resolver->setDefaults(array( 
  9.             'choices' => array( 
  10.                 'm' => 'Male', 
  11.                 'f' => 'Female', 
  12.             ) 
  13.         )); 
  14.     } 
  15.     public function getParent() 
  16.     { 
  17.         return 'choice'; 
  18.     } 
  19.     public function getName() 
  20.     { 
  21.         return 'gender'; 
  22.     } 
The location of this file is not important - the

Form\Type

directory is just a convention.

該檔案的位置并不重要,放在

Form\Type目錄隻是根據慣例。

Here, the return value of the

getParent

function indicates that we're extending the

choice

field type. This means that, by default, we inherit all of the logic and rendering of that field type. To see some of the logic, check out the ChoiceType class. There are three methods that are particularly important:

在這裡,getParent函數的傳回值表明我們正在擴充choice域類型。這意味着,在預設情況下,我們繼承該域類型的全部邏輯和渲染。要檢視該域類型的邏輯,請檢出ChoiceType類。有三個方法是非常重要的:

  • buildForm()

    - Each field type has a

    buildForm

    method, which is where you configure and build any field(s). Notice that this is the same method you use to setup your forms, and it works the same here.

    buildForm()

    - 每個域類型都有一個buildForm方法,在該方法中您可以配置和建構任何表單域。注意這與您設定您表單的方法相同,同時它們的工作原理也相同。
  • buildView()

    - This method is used to set any extra variables you'll need when rendering your field in a template. For example, in ChoiceType, a

    multiple

    variable is set and used in the template to set (or not set) the

    multiple

    attribute on the

    select

    field. See Creating a Template for the Field for more details.

    buildView()

    - 該方法用于設定您在模闆中渲染您表單域所需的任何額外變量。例如,在ChoiceType中multiple變量被設定用來在模闆中設定(或不設定)select表單域的multiple屬性。詳情請參見為表單域建立模闆
  • setDefaultOptions()

    - This defines options for your form type that can be used in

    buildForm()

    and

    buildView()

    . There are a lot of options common to all fields (see form Field Type), but you can create any others that you need here.

    setDefaultOptions()

    - 該方法定義您表單類型的選項,可用于buildForm()和buildView()方法。對于全部表單域來說已經有很多常用選項(參見表單域類型),但您也可以在這裡建立您所需的其它選項。
If you're creating a field that consists of many fields, then be sure to set your "parent" type as

form

or something that extends

form

. Also, if you need to modify the "view" of any of your child types from your parent type, use the

finishView()

method.

如果您正在建立一個由許多表單域組成的表單域,然後您的“父”類型設為表單或者設為表單的擴充。此外,如果您需要修改您父類型下任何子類型的“視圖”,請使用finishView()方法。

The

getName()

method returns an identifier which should be unique in your application. This is used in various places, such as when customizing how your form type will be rendered.

getName()方法傳回一個在您應用程式中唯一的辨別。它将被用于不同的地方,如定制您的表單類型要如何渲染。

The goal of our field was to extend the choice type to enable selection of a gender. This is achieved by fixing the

choices

to a list of possible genders.

我們表單域的目的是要擴充choice類型,以便能選擇性别。這裡通過固定的性别選擇清單來實作。

Creating a Template for the Field

為表單域建立模闆

Each field type is rendered by a template fragment, which is determined in part by the value of your

getName()

method. For more information, see What are Form Themes?.

每個域類型都是通過一個模闆片斷渲染的,而模闆片斷又部分取決于您getName()方法的值。更多資訊請參見什麼是表單主題?。

In this case, since our parent field is

choice

, we don't need to do any work as our custom field type will automatically be rendered like a

choice

type. But for the sake of this example, let's suppose that when our field is "expanded" (i.e. radio buttons or checkboxes, instead of a select field), we want to always render it in a

ul

element. In your form theme template (see above link for details), create a

gender_widget

block to handle this:

在本例中,因為我們的父表單域是choice,是以我們不需要做任何工作,作為我們自定義的域類型會自動按choice類型模闆渲染。但就在這個例子裡,讓我們假定我們的表單域是“擴大”的(如用單選按鈕或複選框來代替select表單域),我們想總是在ul元素中渲染它。那麼在您表單主題模闆中(詳情參見上面的連結),讓我們建立一個gender_widget區塊來處理上述假定:

  1. {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #} 
  2. {% block gender_widget %} 
  3.     {% spaceless %} 
  4.         {% if expanded %} 
  5.             <ul {{ block('widget_container_attributes') }}> 
  6.             {% for child in form %} 
  7.                 <li> 
  8.                     {{ form_widget(child) }} 
  9.                     {{ form_label(child) }} 
  10.                 </li> 
  11.             {% endfor %} 
  12.             </ul> 
  13.         {% else %} 
  14.             {# just let the choice widget render the select tag #} 
  15.             {{ block('choice_widget') }} 
  16.         {% endif %} 
  17.     {% endspaceless %} 
  18. {% endblock %} 
Make sure the correct widget prefix is used. In this example the name should be

gender_widget

, according to the value returned by

getName

. Further, the main config file should point to the custom form template so that it's used when rendering all forms.

確定使用正确的小部件字首。在這個例子中,根據getName傳回的值,小部件的名稱應該是gender_widget。此外,當自定義表單模闆被用于渲染所有表單時,在主配置檔案中應該指向該模闆。

  1. # app/config/config.yml 
  2. twig: 
  3.     form: 
  4.         resources: 
  5.             - 'AcmeDemoBundle:Form:fields.html.twig' 

Using the Field Type

使用域類型

You can now use your custom field type immediately, simply by creating a new instance of the type in one of your forms:

您現在可以馬上使用您的自定義域類型,隻需要簡單地在您表單中建立一個該類型的新執行個體:

  1. // src/Acme/DemoBundle/Form/Type/AuthorType.php 
  2. namespace Acme\DemoBundle\Form\Type; 
  3. use Symfony\Component\Form\AbstractType; 
  4. use Symfony\Component\Form\FormBuilderInterface; 
  5. class AuthorType extends AbstractType 
  6.     public function buildForm(FormBuilderInterface $builder, array $options) 
  7.     { 
  8.         $builder->add('gender_code', new GenderType(), array( 
  9.             'empty_value' => 'Choose a gender', 
  10.         )); 
  11.     } 

But this only works because the

GenderType()

is very simple. What if the gender codes were stored in configuration or in a database? The next section explains how more complex field types solve this problem.

但這僅僅隻是可以工作,因為GenderType()非常簡單。如果gender相關代碼是被儲存在配置檔案或資料庫中呢?下一節将闡述更複雜的域類型将如何解決該問題。

Creating your Field Type as a Service

建立您的域類型服務

So far, this entry has assumed that you have a very simple custom field type. But if you need access to configuration, a database connection, or some other service, then you'll want to register your custom type as a service. For example, suppose that we're storing the gender parameters in configuration:

到目前為止,本文隻是假定您有一個非常簡單的自定義域類型。但如果您需要通路配置、資料庫連接配接或其它服務,您需要将您自定義的類型注冊成服務。舉個例子,假設我們在配置中儲存了gender的參數:

  1. # app/config/config.yml 
  2. parameters: 
  3.     genders: 
  4.         m: Male 
  5.         f: Female 

To use the parameter, we'll define our custom field type as a service, injecting the

genders

parameter value as the first argument to its to-be-created

__construct

function:

要使用該參數,我們需要将我們自定義的域類型定義成服務,并将genders參數值做為它的構造函數__construct的第一個參數注入:

  1. # src/Acme/DemoBundle/Resources/config/services.yml 
  2. services: 
  3.     form.type.gender: 
  4.         class: Acme\DemoBundle\Form\Type\GenderType 
  5.         arguments: 
  6.             - "%genders%" 
  7.         tags: 
  8.             - { name: form.type, alias: gender } 

Make sure the services file is being imported. See Importing Configuration with imports for details.

確定服務檔案已經被導入。詳情參見用imports導入配置

  1. // src/Acme/DemoBundle/Form/Type/GenderType.php 
  2. namespace Acme\DemoBundle\Form\Type; 
  3. use Symfony\Component\OptionsResolver\OptionsResolverInterface; 
  4. // ... 
  5. class GenderType extends AbstractType 
  6.     private $genderChoices; 
  7.     public function __construct(array $genderChoices) 
  8.     { 
  9.         $this->genderChoices = $genderChoices; 
  10.     } 
  11.     public function setDefaultOptions(OptionsResolverInterface $resolver) 
  12.     { 
  13.         $resolver->setDefaults(array( 
  14.             'choices' => $this->genderChoices, 
  15.         )); 
  16.     } 
  17.     // ... 
  1. // src/Acme/DemoBundle/Form/Type/AuthorType.php 
  2. namespace Acme\DemoBundle\Form\Type; 
  3. use Symfony\Component\Form\FormBuilderInterface; 
  4. // ... 
  5. class AuthorType extends AbstractType 
  6.     public function buildForm(FormBuilderInterface $builder, array $options) 
  7.     { 
  8.         $builder->add('gender_code', 'gender', array( 
  9.             'empty_value' => 'Choose a gender', 
  10.         )); 
  11.     }