@@ -7,7 +7,7 @@ How to Create a Custom Form Field Type
77Symfony comes with a bunch of core field types available for building forms.
88However there are situations where you may want to create a custom form field
99type for a specific purpose. This recipe assumes you need a field definition
10- that holds a person's gender , based on the existing choice field. This section
10+ that holds a shipping option , based on the existing choice field. This section
1111explains how the field is defined, how you can customize its layout and finally,
1212how you can register it for use in your application.
1313
@@ -16,25 +16,26 @@ Defining the Field Type
1616
1717In order to create the custom field type, first you have to create the class
1818representing the field. In this situation the class holding the field type
19- will be called ``GenderType `` and the file will be stored in the default location
19+ will be called ``ShippingType `` and the file will be stored in the default location
2020for form fields, which is ``<BundleName>\Form\Type ``. Make sure the field extends
2121:class: `Symfony\\ Component\\ Form\\ AbstractType `::
2222
23- // src/AppBundle/Form/Type/GenderType .php
23+ // src/AppBundle/Form/Type/ShippingType .php
2424 namespace AppBundle\Form\Type;
2525
2626 use Symfony\Component\Form\AbstractType;
2727 use Symfony\Component\OptionsResolver\OptionsResolver;
2828 use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
2929
30- class GenderType extends AbstractType
30+ class ShippingType extends AbstractType
3131 {
3232 public function configureOptions(OptionsResolver $resolver)
3333 {
3434 $resolver->setDefaults(array(
3535 'choices' => array(
36- 'm' => 'Male',
37- 'f' => 'Female',
36+ 'Standard Shipping' => 'standard',
37+ 'Expedited Shipping' => 'expedited',
38+ 'Priority Shipping' => 'priority',
3839 )
3940 ));
4041 }
@@ -65,8 +66,8 @@ important:
6566 This method is used to set any extra variables you'll
6667 need when rendering your field in a template. For example, in `ChoiceType `_,
6768 a ``multiple `` variable is set and used in the template to set (or not
68- set) the ``multiple `` attribute on the ``select `` field. See ` Creating a Template for the Field `_
69- for more details.
69+ set) the ``multiple `` attribute on the ``select `` field. See
70+ ` Creating a Template for the Field `_ for more details.
7071
7172``configureOptions() ``
7273 This defines options for your form type that
@@ -81,9 +82,9 @@ important:
8182 Also, if you need to modify the "view" of any of your child types from
8283 your parent type, use the ``finishView() `` method.
8384
84- The goal of this field was to extend the choice type to enable selection of
85- a gender . This is achieved by fixing the ``choices `` to a list of possible
86- genders .
85+ The goal of this field was to extend the choice type to enable selection of the
86+ shipping type . This is achieved by fixing the ``choices `` to a list of available
87+ shipping options .
8788
8889Creating a Template for the Field
8990---------------------------------
@@ -93,9 +94,9 @@ the class name of your type. For more information, see :ref:`form-customization-
9394
9495.. note ::
9596
96- The first part of the prefix (e.g. ``gender ``) comes from the class name
97- (``GenderType `` -> ``gender ``). This can be controlled by overriding ``getBlockPrefix() ``
98- in ``GenderType ``.
97+ The first part of the prefix (e.g. ``shipping ``) comes from the class name
98+ (``ShippingType `` -> ``shipping ``). This can be controlled by overriding ``getBlockPrefix() ``
99+ in ``ShippingType ``.
99100
100101.. caution ::
101102
@@ -111,14 +112,14 @@ any work as the custom field type will automatically be rendered like a ``Choice
111112But for the sake of this example, suppose that when your field is "expanded"
112113(i.e. radio buttons or checkboxes, instead of a select field), you want to
113114always render it in a ``ul `` element. In your form theme template (see above
114- link for details), create a ``gender_widget `` block to handle this:
115+ link for details), create a ``shipping_widget `` block to handle this:
115116
116117.. configuration-block ::
117118
118119 .. code-block :: html+twig
119120
120121 {# app/Resources/views/form/fields.html.twig #}
121- {% block gender_widget %}
122+ {% block shipping_widget %}
122123 {% spaceless %}
123124 {% if expanded %}
124125 <ul {{ block('widget_container_attributes') }}>
@@ -138,7 +139,7 @@ link for details), create a ``gender_widget`` block to handle this:
138139
139140 .. code-block :: html+php
140141
141- <!-- app/Resources/views/form/gender_widget .html.php -->
142+ <!-- app/Resources/views/form/shipping_widget .html.php -->
142143 <?php if ($expanded) : ?>
143144 <ul <?php $view['form']->block($form, 'widget_container_attributes') ?>>
144145 <?php foreach ($form as $child) : ?>
@@ -156,7 +157,7 @@ link for details), create a ``gender_widget`` block to handle this:
156157.. note ::
157158
158159 Make sure the correct widget prefix is used. In this example the name should
159- be ``gender_widget `` (see :ref: `form-customization-form-themes `).
160+ be ``shipping_widget `` (see :ref: `form-customization-form-themes `).
160161 Further, the main config file should point to the custom form template
161162 so that it's used when rendering all forms.
162163
@@ -253,20 +254,20 @@ new instance of the type in one of your forms::
253254
254255 use Symfony\Component\Form\AbstractType;
255256 use Symfony\Component\Form\FormBuilderInterface;
256- use AppBundle\Form\Type\GenderType ;
257+ use AppBundle\Form\Type\ShippingType ;
257258
258- class AuthorType extends AbstractType
259+ class OrderType extends AbstractType
259260 {
260261 public function buildForm(FormBuilderInterface $builder, array $options)
261262 {
262- $builder->add('gender_code ', GenderType ::class, array(
263- 'placeholder' => 'Choose a gender ',
263+ $builder->add('shipping_code ', ShippingType ::class, array(
264+ 'placeholder' => 'Choose a delivery option ',
264265 ));
265266 }
266267 }
267268
268- But this only works because the ``GenderType `` is very simple. What if
269- the gender codes were stored in configuration or in a database? The next
269+ But this only works because the ``ShippingType() `` is very simple. What if
270+ the shipping codes were stored in configuration or in a database? The next
270271section explains how more complex field types solve this problem.
271272
272273.. _form-field-service :
@@ -277,17 +278,18 @@ Creating your Field Type as a Service
277278So far, this entry has assumed that you have a very simple custom field type.
278279But if you need access to configuration, a database connection, or some other
279280service, then you'll want to register your custom type as a service. For
280- example, suppose that you're storing the gender parameters in configuration:
281+ example, suppose that you're storing the shipping parameters in configuration:
281282
282283.. configuration-block ::
283284
284285 .. code-block :: yaml
285286
286287 # app/config/config.yml
287288 parameters :
288- genders :
289- m : Male
290- f : Female
289+ shipping_options :
290+ standard : Standard Shipping
291+ expedited : Expedited Shipping
292+ priority : Priority Shipping
291293
292294 .. code-block :: xml
293295
@@ -299,21 +301,25 @@ example, suppose that you're storing the gender parameters in configuration:
299301 http://symfony.com/schema/dic/services/services-1.0.xsd" >
300302
301303 <parameters >
302- <parameter key =" genders" type =" collection" >
303- <parameter key =" m" >Male</parameter >
304- <parameter key =" f" >Female</parameter >
304+ <parameter key =" shipping_options" type =" collection" >
305+ <parameter key =" standard" >Standard Shipping</parameter >
306+ <parameter key =" expedited" >Expedited Shipping</parameter >
307+ <parameter key =" priority" >Priority Shipping</parameter >
305308 </parameter >
306309 </parameters >
307310 </container >
308311
309312 .. code-block :: php
310313
311314 // app/config/config.php
312- $container->setParameter('genders.m', 'Male');
313- $container->setParameter('genders.f', 'Female');
314-
315- To use the parameter, define your custom field type as a service, injecting
316- the ``genders `` parameter value as the first argument to its to-be-created
315+ $container->setParameter('shipping_options', array(
316+ 'standard' => 'Standard Shipping',
317+ 'expedited' => 'Expedited Shipping',
318+ 'priority' => 'Priority Shipping',
319+ ));
320+
321+ To use the parameter, define your custom field type as a service, injecting the
322+ ``shipping_options `` parameter value as the first argument to its to-be-created
317323``__construct() `` function:
318324
319325.. configuration-block ::
@@ -322,10 +328,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
322328
323329 # src/AppBundle/Resources/config/services.yml
324330 services :
325- app.form.type.gender :
326- class : AppBundle\Form\Type\GenderType
331+ app.form.type.shipping :
332+ class : AppBundle\Form\Type\ShippingType
327333 arguments :
328- - ' %genders %'
334+ - ' %shipping_options %'
329335 tags :
330336 - { name: form.type }
331337
@@ -339,8 +345,8 @@ the ``genders`` parameter value as the first argument to its to-be-created
339345 http://symfony.com/schema/dic/services/services-1.0.xsd" >
340346
341347 <services >
342- <service id =" app.form.type.gender " class =" AppBundle\Form\Type\GenderType " >
343- <argument >%genders %</argument >
348+ <service id =" app.form.type.shipping " class =" AppBundle\Form\Type\ShippingType " >
349+ <argument >%shipping_options %</argument >
344350 <tag name =" form.type" />
345351 </service >
346352 </services >
@@ -349,10 +355,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
349355 .. code-block :: php
350356
351357 // src/AppBundle/Resources/config/services.php
352- use AppBundle\Form\Type\GenderType ;
358+ use AppBundle\Form\Type\ShippingType ;
353359
354- $container->register('app.form.type.gender ', GenderType ::class)
355- ->addArgument('%genders %')
360+ $container->register('app.form.type.shipping ', ShippingType ::class)
361+ ->addArgument('%shipping_options %')
356362 ->addTag('form.type')
357363 ;
358364
@@ -361,54 +367,54 @@ the ``genders`` parameter value as the first argument to its to-be-created
361367 Make sure the services file is being imported. See :ref: `service-container-imports-directive `
362368 for details.
363369
364- First, add a ``__construct `` method to ``GenderType ``, which receives the gender
365- configuration::
370+ First, add a ``__construct `` method to ``ShippingType ``, which receives the
371+ shipping configuration::
366372
367- // src/AppBundle/Form/Type/GenderType .php
373+ // src/AppBundle/Form/Type/ShippingType .php
368374 namespace AppBundle\Form\Type;
369375
370376 use Symfony\Component\OptionsResolver\OptionsResolver;
371377
372378 // ...
373379
374380 // ...
375- class GenderType extends AbstractType
381+ class ShippingType extends AbstractType
376382 {
377- private $genderChoices ;
383+ private $shippingOptions ;
378384
379- public function __construct(array $genderChoices )
385+ public function __construct(array $shippingOptions )
380386 {
381- $this->genderChoices = $genderChoices ;
387+ $this->shippingOptions = $shippingOptions ;
382388 }
383389
384390 public function configureOptions(OptionsResolver $resolver)
385391 {
386392 $resolver->setDefaults(array(
387- 'choices' => $this->genderChoices ,
393+ 'choices' => array_flip( $this->shippingOptions) ,
388394 ));
389395 }
390396
391397 // ...
392398 }
393399
394- Great! The ``GenderType `` is now fueled by the configuration parameters and
400+ Great! The ``ShippingType `` is now fueled by the configuration parameters and
395401registered as a service. Because you used the ``form.type `` tag in its configuration,
396- your service will be used instead of creating a *new * ``GenderType ``. In other words,
402+ your service will be used instead of creating a *new * ``ShippingType ``. In other words,
397403your controller *does not need to change *, it still looks like this::
398404
399405 // src/AppBundle/Form/Type/AuthorType.php
400406 namespace AppBundle\Form\Type;
401407
402408 use Symfony\Component\Form\AbstractType;
403409 use Symfony\Component\Form\FormBuilderInterface;
404- use AppBundle\Form\Type\GenderType ;
410+ use AppBundle\Form\Type\ShippingType ;
405411
406- class AuthorType extends AbstractType
412+ class OrderType extends AbstractType
407413 {
408414 public function buildForm(FormBuilderInterface $builder, array $options)
409415 {
410- $builder->add('gender_code ', GenderType ::class, array(
411- 'placeholder' => 'Choose a gender ',
416+ $builder->add('shipping_code ', ShippingType ::class, array(
417+ 'placeholder' => 'Choose a delivery option ',
412418 ));
413419 }
414420 }
0 commit comments