@@ -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 }
@@ -69,8 +70,8 @@ important:
6970 This method is used to set any extra variables you'll
7071 need when rendering your field in a template. For example, in `ChoiceType `_,
7172 a ``multiple `` variable is set and used in the template to set (or not
72- set) the ``multiple `` attribute on the ``select `` field. See ` Creating a Template for the Field `_
73- for more details.
73+ set) the ``multiple `` attribute on the ``select `` field. See
74+ ` Creating a Template for the Field `_ for more details.
7475
7576.. versionadded :: 2.7
7677 The ``configureOptions() `` method was introduced in Symfony 2.7. Previously,
@@ -89,9 +90,9 @@ important:
8990 Also, if you need to modify the "view" of any of your child types from
9091 your parent type, use the ``finishView() `` method.
9192
92- The goal of this field was to extend the choice type to enable selection of
93- a gender . This is achieved by fixing the ``choices `` to a list of possible
94- genders .
93+ The goal of this field was to extend the choice type to enable selection of the
94+ shipping type . This is achieved by fixing the ``choices `` to a list of available
95+ shipping options .
9596
9697Creating a Template for the Field
9798---------------------------------
@@ -101,9 +102,9 @@ the class name of your type. For more information, see :ref:`form-customization-
101102
102103.. note ::
103104
104- The first part of the prefix (e.g. ``gender ``) comes from the class name
105- (``GenderType `` -> ``gender ``). This can be controlled by overriding ``getBlockPrefix() ``
106- in ``GenderType ``.
105+ The first part of the prefix (e.g. ``shipping ``) comes from the class name
106+ (``ShippingType `` -> ``shipping ``). This can be controlled by overriding ``getBlockPrefix() ``
107+ in ``ShippingType ``.
107108
108109.. caution ::
109110
@@ -119,14 +120,14 @@ any work as the custom field type will automatically be rendered like a ``Choice
119120But for the sake of this example, suppose that when your field is "expanded"
120121(i.e. radio buttons or checkboxes, instead of a select field), you want to
121122always render it in a ``ul `` element. In your form theme template (see above
122- link for details), create a ``gender_widget `` block to handle this:
123+ link for details), create a ``shipping_widget `` block to handle this:
123124
124125.. configuration-block ::
125126
126127 .. code-block :: html+twig
127128
128129 {# app/Resources/views/form/fields.html.twig #}
129- {% block gender_widget %}
130+ {% block shipping_widget %}
130131 {% spaceless %}
131132 {% if expanded %}
132133 <ul {{ block('widget_container_attributes') }}>
@@ -146,7 +147,7 @@ link for details), create a ``gender_widget`` block to handle this:
146147
147148 .. code-block :: html+php
148149
149- <!-- app/Resources/views/form/gender_widget .html.php -->
150+ <!-- app/Resources/views/form/shipping_widget .html.php -->
150151 <?php if ($expanded) : ?>
151152 <ul <?php $view['form']->block($form, 'widget_container_attributes') ?>>
152153 <?php foreach ($form as $child) : ?>
@@ -164,7 +165,7 @@ link for details), create a ``gender_widget`` block to handle this:
164165.. note ::
165166
166167 Make sure the correct widget prefix is used. In this example the name should
167- be ``gender_widget `` (see :ref: `form-customization-form-themes `).
168+ be ``shipping_widget `` (see :ref: `form-customization-form-themes `).
168169 Further, the main config file should point to the custom form template
169170 so that it's used when rendering all forms.
170171
@@ -261,20 +262,20 @@ new instance of the type in one of your forms::
261262
262263 use Symfony\Component\Form\AbstractType;
263264 use Symfony\Component\Form\FormBuilderInterface;
264- use AppBundle\Form\Type\GenderType ;
265+ use AppBundle\Form\Type\ShippingType ;
265266
266- class AuthorType extends AbstractType
267+ class OrderType extends AbstractType
267268 {
268269 public function buildForm(FormBuilderInterface $builder, array $options)
269270 {
270- $builder->add('gender_code ', GenderType ::class, array(
271- 'placeholder' => 'Choose a gender ',
271+ $builder->add('shipping_code ', ShippingType ::class, array(
272+ 'placeholder' => 'Choose a delivery option ',
272273 ));
273274 }
274275 }
275276
276- But this only works because the ``GenderType `` is very simple. What if
277- the gender codes were stored in configuration or in a database? The next
277+ But this only works because the ``ShippingType() `` is very simple. What if
278+ the shipping codes were stored in configuration or in a database? The next
278279section explains how more complex field types solve this problem.
279280
280281.. _form-field-service :
@@ -285,17 +286,18 @@ Creating your Field Type as a Service
285286So far, this entry has assumed that you have a very simple custom field type.
286287But if you need access to configuration, a database connection, or some other
287288service, then you'll want to register your custom type as a service. For
288- example, suppose that you're storing the gender parameters in configuration:
289+ example, suppose that you're storing the shipping parameters in configuration:
289290
290291.. configuration-block ::
291292
292293 .. code-block :: yaml
293294
294295 # app/config/config.yml
295296 parameters :
296- genders :
297- m : Male
298- f : Female
297+ shipping_options :
298+ standard : Standard Shipping
299+ expedited : Expedited Shipping
300+ priority : Priority Shipping
299301
300302 .. code-block :: xml
301303
@@ -307,21 +309,25 @@ example, suppose that you're storing the gender parameters in configuration:
307309 http://symfony.com/schema/dic/services/services-1.0.xsd" >
308310
309311 <parameters >
310- <parameter key =" genders" type =" collection" >
311- <parameter key =" m" >Male</parameter >
312- <parameter key =" f" >Female</parameter >
312+ <parameter key =" shipping_options" type =" collection" >
313+ <parameter key =" standard" >Standard Shipping</parameter >
314+ <parameter key =" expedited" >Expedited Shipping</parameter >
315+ <parameter key =" priority" >Priority Shipping</parameter >
313316 </parameter >
314317 </parameters >
315318 </container >
316319
317320 .. code-block :: php
318321
319322 // app/config/config.php
320- $container->setParameter('genders.m', 'Male');
321- $container->setParameter('genders.f', 'Female');
322-
323- To use the parameter, define your custom field type as a service, injecting
324- the ``genders `` parameter value as the first argument to its to-be-created
323+ $container->setParameter('shipping_options', array(
324+ 'standard' => 'Standard Shipping',
325+ 'expedited' => 'Expedited Shipping',
326+ 'priority' => 'Priority Shipping',
327+ ));
328+
329+ To use the parameter, define your custom field type as a service, injecting the
330+ ``shipping_options `` parameter value as the first argument to its to-be-created
325331``__construct() `` function:
326332
327333.. configuration-block ::
@@ -330,10 +336,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
330336
331337 # src/AppBundle/Resources/config/services.yml
332338 services :
333- app.form.type.gender :
334- class : AppBundle\Form\Type\GenderType
339+ app.form.type.shipping :
340+ class : AppBundle\Form\Type\ShippingType
335341 arguments :
336- - ' %genders %'
342+ - ' %shipping_options %'
337343 tags :
338344 - { name: form.type }
339345
@@ -347,8 +353,8 @@ the ``genders`` parameter value as the first argument to its to-be-created
347353 http://symfony.com/schema/dic/services/services-1.0.xsd" >
348354
349355 <services >
350- <service id =" app.form.type.gender " class =" AppBundle\Form\Type\GenderType " >
351- <argument >%genders %</argument >
356+ <service id =" app.form.type.shipping " class =" AppBundle\Form\Type\ShippingType " >
357+ <argument >%shipping_options %</argument >
352358 <tag name =" form.type" />
353359 </service >
354360 </services >
@@ -357,10 +363,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
357363 .. code-block :: php
358364
359365 // src/AppBundle/Resources/config/services.php
360- use AppBundle\Form\Type\GenderType ;
366+ use AppBundle\Form\Type\ShippingType ;
361367
362- $container->register('app.form.type.gender ', GenderType ::class)
363- ->addArgument('%genders %')
368+ $container->register('app.form.type.shipping ', ShippingType ::class)
369+ ->addArgument('%shipping_options %')
364370 ->addTag('form.type')
365371 ;
366372
@@ -369,54 +375,54 @@ the ``genders`` parameter value as the first argument to its to-be-created
369375 Make sure the services file is being imported. See :ref: `service-container-imports-directive `
370376 for details.
371377
372- First, add a ``__construct `` method to ``GenderType ``, which receives the gender
373- configuration::
378+ First, add a ``__construct `` method to ``ShippingType ``, which receives the
379+ shipping configuration::
374380
375- // src/AppBundle/Form/Type/GenderType .php
381+ // src/AppBundle/Form/Type/ShippingType .php
376382 namespace AppBundle\Form\Type;
377383
378384 use Symfony\Component\OptionsResolver\OptionsResolver;
379385
380386 // ...
381387
382388 // ...
383- class GenderType extends AbstractType
389+ class ShippingType extends AbstractType
384390 {
385- private $genderChoices ;
391+ private $shippingOptions ;
386392
387- public function __construct(array $genderChoices )
393+ public function __construct(array $shippingOptions )
388394 {
389- $this->genderChoices = $genderChoices ;
395+ $this->shippingOptions = $shippingOptions ;
390396 }
391397
392398 public function configureOptions(OptionsResolver $resolver)
393399 {
394400 $resolver->setDefaults(array(
395- 'choices' => $this->genderChoices ,
401+ 'choices' => array_flip( $this->shippingOptions) ,
396402 ));
397403 }
398404
399405 // ...
400406 }
401407
402- Great! The ``GenderType `` is now fueled by the configuration parameters and
408+ Great! The ``ShippingType `` is now fueled by the configuration parameters and
403409registered as a service. Because you used the ``form.type `` tag in its configuration,
404- your service will be used instead of creating a *new * ``GenderType ``. In other words,
410+ your service will be used instead of creating a *new * ``ShippingType ``. In other words,
405411your controller *does not need to change *, it still looks like this::
406412
407413 // src/AppBundle/Form/Type/AuthorType.php
408414 namespace AppBundle\Form\Type;
409415
410416 use Symfony\Component\Form\AbstractType;
411417 use Symfony\Component\Form\FormBuilderInterface;
412- use AppBundle\Form\Type\GenderType ;
418+ use AppBundle\Form\Type\ShippingType ;
413419
414- class AuthorType extends AbstractType
420+ class OrderType extends AbstractType
415421 {
416422 public function buildForm(FormBuilderInterface $builder, array $options)
417423 {
418- $builder->add('gender_code ', GenderType ::class, array(
419- 'placeholder' => 'Choose a gender ',
424+ $builder->add('shipping_code ', ShippingType ::class, array(
425+ 'placeholder' => 'Choose a delivery option ',
420426 ));
421427 }
422428 }
0 commit comments