@@ -7,69 +7,87 @@ How to Create a Custom Form Field Type
7
7
Symfony comes with a bunch of core field types available for building forms.
8
8
However there are situations where we want to create a custom form field
9
9
type for a specific purpose. In this recipe we assume we need a field definition
10
- that holds a person's full name, consisting of two inputs: the first name
11
- and the last name. Of course we can achieve this by using two separate fields,
12
- but let's assume we want to have a single field definition holding this
13
- information. This section explains how the field is defined, how we can customize
14
- its layout and finally, how we can register it to use it in our application.
10
+ that holds a person's gender, based on the existing choice field. This section
11
+ explains how the field is defined, how we can customize its layout and finally,
12
+ how we can register it to use it in our application.
15
13
16
14
Defining the Field Type
17
15
--------------------------
18
16
19
17
In order to create the custom field type, first we have to create the class
20
18
representing the field. In our situation the class holding the field type
21
- will be called FullnameType and the file will be stored in the default location
19
+ will be called GenderType and the file will be stored in the default location
22
20
for form fields, which is <BundleName>\F orm\T ype. Make sure the field extends
23
21
AbstractType.
24
22
25
23
.. code-block :: php
26
24
27
- # src/Acme/DemoBundle/Form/Type/FullnameType .php
25
+ # src/Acme/DemoBundle/Form/Type/GenderType .php
28
26
29
27
namespace Acme\DemoBundle\Form\Type;
30
28
31
29
use Symfony\Component\Form\AbstractType;
32
30
use Symfony\Component\Form\FormBuilder;
33
31
34
- class FullnameType extends AbstractType
32
+ class GenderType extends AbstractType
35
33
{
36
34
/**
37
35
* {@inheritdoc}
38
36
*/
39
- public function buildForm(FormBuilder $builder, array $options)
37
+ public function getDefaultOptions( array $options)
40
38
{
41
- $builder
42
- ->add('firstName', 'text', array(
43
- 'attr' => array(
44
- 'class' => 'firstName'
45
- )
46
- ))
47
- ->add('lastName', 'text', array(
48
- 'attr' => array(
49
- 'class' => 'lastName'
50
- )
51
- ))
52
- ;
39
+ return array(
40
+ 'choice_list' => new GenderChoiceList(),
41
+ );
42
+ }
43
+
44
+ /**
45
+ * {@inheritdoc}
46
+ */
47
+ public function getParent(array $options)
48
+ {
49
+ return 'choice';
53
50
}
54
51
55
52
/**
56
53
* {@inheritdoc}
57
54
*/
58
55
public function getName()
59
56
{
60
- return 'fullname ';
57
+ return 'gender ';
61
58
}
62
59
63
60
}
64
61
65
- Here we can see from the contents of the buildForm function that the field
66
- itself is composed of two fields holding the first name and the last name.
67
- This will lead to two separate inputs within your field.
68
-
69
- The getName method returns an identifier for the type which is used
62
+ Here the return value of the getParent function indicates that the choice
63
+ field is extended. The getName method returns an identifier which is used
70
64
to prevent conflicts with other types. For shared bundles, a good practice
71
65
is to start the type name with the bundle alias.
72
66
67
+ The goal of our field was to extend the choice type to enable selection of
68
+ a gender. This is achieved by fixing the choice_list to a list of possible
69
+ genders. As the choice field uses a ChoiceList for building its list, we
70
+ create one that provides this information.
71
+
72
+ .. code-block :: php
73
+
74
+ # src/Acme/DemoBundle/Form/ChoiceList/GenderChoiceList.php
75
+
76
+ namespace Acme\DemoBundle\Form\ChoiceList;
77
+
78
+ use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
79
+
80
+ class GenderChoiceList implements ChoiceListInterface
81
+ {
82
+ public function getChoices()
83
+ {
84
+ return array(
85
+ 'm' => 'male',
86
+ 'f' => 'female',
87
+ );
88
+ }
89
+ }
90
+
73
91
Creating a Template for the Field
74
92
---------------------------------
75
93
@@ -83,42 +101,26 @@ form_div_layout.html.twig (if it does not exist yet) and add our new field.
83
101
84
102
# src/Acme/DemoBundle/Resources/Form/fields.html.twig
85
103
86
- {% extends 'form_div_layout.html.twig' %}
87
-
88
- {% block fullname_widget %}
104
+ {% block gender_widget %}
89
105
<div {{ block('widget_container_attributes') }}>
90
- {{ form_errors(form.firstName) }}
91
- {{ form_errors(form.lastName) }}
92
- {{ form_widget(form.firstName) }} {{ form_widget(form.lastName) }}
106
+ {{ form_errors(form) }}
107
+ {{ form_widget(form) }}
93
108
</div>
94
109
{% endblock %}
95
110
96
111
Make sure the correct widget prefix is used. In this example the name should
97
- be fullname_widget , according to the value returned by getName. Further,
98
- the configuration should point to the custom fields template. Otherwise
112
+ be gender_widget , according to the value returned by getName. Further,
113
+ the main config file should point to the custom fields template. Otherwise
99
114
the default form template will be used.
100
115
101
- .. configuration-block ::
102
-
103
- .. code-block :: yaml
116
+ .. code-block :: yaml
104
117
105
- # src/Acme/DemoBundle/Resources/config/resources.yml
106
-
107
- twig :
108
- form :
109
- resources :
110
- - ' AcmeDemoBundle:Form:fields.html.twig'
111
-
112
- .. code-block :: xml
113
-
114
- # src/Acme/DemoBundle/Resources/config/resources.xml
118
+ # app/config/config.yml
115
119
116
- <twig : config ...>
117
- <twig : form >
118
- <resource >AcmeDemoBundle:Form:fields.html.twig</resource >
119
- </twig : form >
120
- <!-- ... -->
121
- </twig : config >
120
+ twig :
121
+ form :
122
+ resources :
123
+ - ' AcmeDemoBundle:Form:fields.html.twig'
122
124
123
125
Registering the Field Type
124
126
--------------------------
@@ -132,17 +134,17 @@ able to use it in our forms. This is achieved by adding it as a new service.
132
134
133
135
# src/Acme/DemoBundle/Resources/config/resources.yml
134
136
135
- form.type.fullname :
137
+ form.type.gender :
136
138
class : Acme\DemoBundle\Form\Type\FullnameType
137
139
tags :
138
- - { name: form.type, alias: fullname }
140
+ - { name: form.type, alias: gender }
139
141
140
142
.. code-block :: xml
141
143
142
144
# src/Acme/DemoBundle/Resources/config/resources.xml
143
145
144
- <service id =" form.type.fullname " class =" Acme\DemoBundle\Form\Type\FullnameType " >
145
- <tag name =" form.type" alias =" fullname " />
146
+ <service id =" form.type.gender " class =" Acme\DemoBundle\Form\Type\GenderType " >
147
+ <tag name =" form.type" alias =" gender " />
146
148
</service >
147
149
148
150
Make sure that the alias tag corresponds with the value returned by the getName
0 commit comments