@@ -7,7 +7,7 @@ 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 you may want to create a custom form field
9
9
type 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
11
11
explains how the field is defined, how you can customize its layout and finally,
12
12
how you can register it for use in your application.
13
13
@@ -16,25 +16,26 @@ Defining the Field Type
16
16
17
17
In order to create the custom field type, first you have to create the class
18
18
representing 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
20
20
for form fields, which is ``<BundleName>\Form\Type ``. Make sure the field extends
21
21
:class: `Symfony\\ Component\\ Form\\ AbstractType `::
22
22
23
- // src/AppBundle/Form/Type/GenderType .php
23
+ // src/AppBundle/Form/Type/ShippingType .php
24
24
namespace AppBundle\Form\Type;
25
25
26
26
use Symfony\Component\Form\AbstractType;
27
27
use Symfony\Component\OptionsResolver\OptionsResolver;
28
28
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
29
29
30
- class GenderType extends AbstractType
30
+ class ShippingType extends AbstractType
31
31
{
32
32
public function configureOptions(OptionsResolver $resolver)
33
33
{
34
34
$resolver->setDefaults(array(
35
35
'choices' => array(
36
- 'm' => 'Male',
37
- 'f' => 'Female',
36
+ 'Standard Shipping' => 'standard',
37
+ 'Expedited Shipping' => 'expedited',
38
+ 'Priority Shipping' => 'priority',
38
39
)
39
40
));
40
41
}
@@ -69,8 +70,8 @@ important:
69
70
This method is used to set any extra variables you'll
70
71
need when rendering your field in a template. For example, in `ChoiceType `_,
71
72
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.
74
75
75
76
.. versionadded :: 2.7
76
77
The ``configureOptions() `` method was introduced in Symfony 2.7. Previously,
@@ -89,9 +90,9 @@ important:
89
90
Also, if you need to modify the "view" of any of your child types from
90
91
your parent type, use the ``finishView() `` method.
91
92
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 .
95
96
96
97
Creating a Template for the Field
97
98
---------------------------------
@@ -101,9 +102,9 @@ the class name of your type. For more information, see :ref:`form-customization-
101
102
102
103
.. note ::
103
104
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 ``.
107
108
108
109
.. caution ::
109
110
@@ -119,14 +120,14 @@ any work as the custom field type will automatically be rendered like a ``Choice
119
120
But for the sake of this example, suppose that when your field is "expanded"
120
121
(i.e. radio buttons or checkboxes, instead of a select field), you want to
121
122
always 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:
123
124
124
125
.. configuration-block ::
125
126
126
127
.. code-block :: html+twig
127
128
128
129
{# app/Resources/views/form/fields.html.twig #}
129
- {% block gender_widget %}
130
+ {% block shipping_widget %}
130
131
{% spaceless %}
131
132
{% if expanded %}
132
133
<ul {{ block('widget_container_attributes') }}>
@@ -146,7 +147,7 @@ link for details), create a ``gender_widget`` block to handle this:
146
147
147
148
.. code-block :: html+php
148
149
149
- <!-- app/Resources/views/form/gender_widget .html.php -->
150
+ <!-- app/Resources/views/form/shipping_widget .html.php -->
150
151
<?php if ($expanded) : ?>
151
152
<ul <?php $view['form']->block($form, 'widget_container_attributes') ?>>
152
153
<?php foreach ($form as $child) : ?>
@@ -164,7 +165,7 @@ link for details), create a ``gender_widget`` block to handle this:
164
165
.. note ::
165
166
166
167
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 `).
168
169
Further, the main config file should point to the custom form template
169
170
so that it's used when rendering all forms.
170
171
@@ -261,20 +262,20 @@ new instance of the type in one of your forms::
261
262
262
263
use Symfony\Component\Form\AbstractType;
263
264
use Symfony\Component\Form\FormBuilderInterface;
264
- use AppBundle\Form\Type\GenderType ;
265
+ use AppBundle\Form\Type\ShippingType ;
265
266
266
- class AuthorType extends AbstractType
267
+ class OrderType extends AbstractType
267
268
{
268
269
public function buildForm(FormBuilderInterface $builder, array $options)
269
270
{
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 ',
272
273
));
273
274
}
274
275
}
275
276
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
278
279
section explains how more complex field types solve this problem.
279
280
280
281
.. _form-field-service :
@@ -285,17 +286,18 @@ Creating your Field Type as a Service
285
286
So far, this entry has assumed that you have a very simple custom field type.
286
287
But if you need access to configuration, a database connection, or some other
287
288
service, 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:
289
290
290
291
.. configuration-block ::
291
292
292
293
.. code-block :: yaml
293
294
294
295
# app/config/config.yml
295
296
parameters :
296
- genders :
297
- m : Male
298
- f : Female
297
+ shipping_options :
298
+ standard : Standard Shipping
299
+ expedited : Expedited Shipping
300
+ priority : Priority Shipping
299
301
300
302
.. code-block :: xml
301
303
@@ -307,21 +309,25 @@ example, suppose that you're storing the gender parameters in configuration:
307
309
http://symfony.com/schema/dic/services/services-1.0.xsd" >
308
310
309
311
<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 >
313
316
</parameter >
314
317
</parameters >
315
318
</container >
316
319
317
320
.. code-block :: php
318
321
319
322
// 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
325
331
``__construct() `` function:
326
332
327
333
.. configuration-block ::
@@ -330,10 +336,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
330
336
331
337
# src/AppBundle/Resources/config/services.yml
332
338
services :
333
- app.form.type.gender :
334
- class : AppBundle\Form\Type\GenderType
339
+ app.form.type.shipping :
340
+ class : AppBundle\Form\Type\ShippingType
335
341
arguments :
336
- - ' %genders %'
342
+ - ' %shipping_options %'
337
343
tags :
338
344
- { name: form.type }
339
345
@@ -347,8 +353,8 @@ the ``genders`` parameter value as the first argument to its to-be-created
347
353
http://symfony.com/schema/dic/services/services-1.0.xsd" >
348
354
349
355
<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 >
352
358
<tag name =" form.type" />
353
359
</service >
354
360
</services >
@@ -357,10 +363,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
357
363
.. code-block :: php
358
364
359
365
// src/AppBundle/Resources/config/services.php
360
- use AppBundle\Form\Type\GenderType ;
366
+ use AppBundle\Form\Type\ShippingType ;
361
367
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 %')
364
370
->addTag('form.type')
365
371
;
366
372
@@ -369,54 +375,54 @@ the ``genders`` parameter value as the first argument to its to-be-created
369
375
Make sure the services file is being imported. See :ref: `service-container-imports-directive `
370
376
for details.
371
377
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::
374
380
375
- // src/AppBundle/Form/Type/GenderType .php
381
+ // src/AppBundle/Form/Type/ShippingType .php
376
382
namespace AppBundle\Form\Type;
377
383
378
384
use Symfony\Component\OptionsResolver\OptionsResolver;
379
385
380
386
// ...
381
387
382
388
// ...
383
- class GenderType extends AbstractType
389
+ class ShippingType extends AbstractType
384
390
{
385
- private $genderChoices ;
391
+ private $shippingOptions ;
386
392
387
- public function __construct(array $genderChoices )
393
+ public function __construct(array $shippingOptions )
388
394
{
389
- $this->genderChoices = $genderChoices ;
395
+ $this->shippingOptions = $shippingOptions ;
390
396
}
391
397
392
398
public function configureOptions(OptionsResolver $resolver)
393
399
{
394
400
$resolver->setDefaults(array(
395
- 'choices' => $this->genderChoices ,
401
+ 'choices' => array_flip( $this->shippingOptions) ,
396
402
));
397
403
}
398
404
399
405
// ...
400
406
}
401
407
402
- Great! The ``GenderType `` is now fueled by the configuration parameters and
408
+ Great! The ``ShippingType `` is now fueled by the configuration parameters and
403
409
registered 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,
405
411
your controller *does not need to change *, it still looks like this::
406
412
407
413
// src/AppBundle/Form/Type/AuthorType.php
408
414
namespace AppBundle\Form\Type;
409
415
410
416
use Symfony\Component\Form\AbstractType;
411
417
use Symfony\Component\Form\FormBuilderInterface;
412
- use AppBundle\Form\Type\GenderType ;
418
+ use AppBundle\Form\Type\ShippingType ;
413
419
414
- class AuthorType extends AbstractType
420
+ class OrderType extends AbstractType
415
421
{
416
422
public function buildForm(FormBuilderInterface $builder, array $options)
417
423
{
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 ',
420
426
));
421
427
}
422
428
}
0 commit comments