Skip to content

Commit 1eee777

Browse files
endroidweaverryan
authored andcommitted
Changed to simple more real-life example and processed feedback concerning theming and configuration
1 parent e6fb4ef commit 1eee777

File tree

1 file changed

+60
-58
lines changed

1 file changed

+60
-58
lines changed

cookbook/form/create_custom_field_type.rst

+60-58
Original file line numberDiff line numberDiff line change
@@ -7,69 +7,87 @@ How to Create a Custom Form Field Type
77
Symfony comes with a bunch of core field types available for building forms.
88
However there are situations where we want to create a custom form field
99
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.
1513

1614
Defining the Field Type
1715
--------------------------
1816

1917
In order to create the custom field type, first we have to create the class
2018
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
2220
for form fields, which is <BundleName>\Form\Type. Make sure the field extends
2321
AbstractType.
2422

2523
.. code-block:: php
2624
27-
# src/Acme/DemoBundle/Form/Type/FullnameType.php
25+
# src/Acme/DemoBundle/Form/Type/GenderType.php
2826
2927
namespace Acme\DemoBundle\Form\Type;
3028
3129
use Symfony\Component\Form\AbstractType;
3230
use Symfony\Component\Form\FormBuilder;
3331
34-
class FullnameType extends AbstractType
32+
class GenderType extends AbstractType
3533
{
3634
/**
3735
* {@inheritdoc}
3836
*/
39-
public function buildForm(FormBuilder $builder, array $options)
37+
public function getDefaultOptions(array $options)
4038
{
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';
5350
}
5451
5552
/**
5653
* {@inheritdoc}
5754
*/
5855
public function getName()
5956
{
60-
return 'fullname';
57+
return 'gender';
6158
}
6259
6360
}
6461
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
7064
to prevent conflicts with other types. For shared bundles, a good practice
7165
is to start the type name with the bundle alias.
7266

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+
7391
Creating a Template for the Field
7492
---------------------------------
7593

@@ -83,42 +101,26 @@ form_div_layout.html.twig (if it does not exist yet) and add our new field.
83101

84102
# src/Acme/DemoBundle/Resources/Form/fields.html.twig
85103

86-
{% extends 'form_div_layout.html.twig' %}
87-
88-
{% block fullname_widget %}
104+
{% block gender_widget %}
89105
<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) }}
93108
</div>
94109
{% endblock %}
95110

96111
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
99114
the default form template will be used.
100115

101-
.. configuration-block::
102-
103-
.. code-block:: yaml
116+
.. code-block:: yaml
104117
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
115119
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'
122124
123125
Registering the Field Type
124126
--------------------------
@@ -132,17 +134,17 @@ able to use it in our forms. This is achieved by adding it as a new service.
132134
133135
# src/Acme/DemoBundle/Resources/config/resources.yml
134136
135-
form.type.fullname:
137+
form.type.gender:
136138
class: Acme\DemoBundle\Form\Type\FullnameType
137139
tags:
138-
- { name: form.type, alias: fullname }
140+
- { name: form.type, alias: gender }
139141
140142
.. code-block:: xml
141143
142144
# src/Acme/DemoBundle/Resources/config/resources.xml
143145
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" />
146148
</service>
147149
148150
Make sure that the alias tag corresponds with the value returned by the getName

0 commit comments

Comments
 (0)