@@ -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
}
@@ -65,8 +66,8 @@ important:
65
66
This method is used to set any extra variables you'll
66
67
need when rendering your field in a template. For example, in `ChoiceType `_,
67
68
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.
70
71
71
72
``configureOptions() ``
72
73
This defines options for your form type that
@@ -81,9 +82,9 @@ important:
81
82
Also, if you need to modify the "view" of any of your child types from
82
83
your parent type, use the ``finishView() `` method.
83
84
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 .
87
88
88
89
Creating a Template for the Field
89
90
---------------------------------
@@ -93,9 +94,9 @@ the class name of your type. For more information, see :ref:`form-customization-
93
94
94
95
.. note ::
95
96
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 ``.
99
100
100
101
.. caution ::
101
102
@@ -111,14 +112,14 @@ any work as the custom field type will automatically be rendered like a ``Choice
111
112
But for the sake of this example, suppose that when your field is "expanded"
112
113
(i.e. radio buttons or checkboxes, instead of a select field), you want to
113
114
always 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:
115
116
116
117
.. configuration-block ::
117
118
118
119
.. code-block :: html+twig
119
120
120
121
{# app/Resources/views/form/fields.html.twig #}
121
- {% block gender_widget %}
122
+ {% block shipping_widget %}
122
123
{% spaceless %}
123
124
{% if expanded %}
124
125
<ul {{ block('widget_container_attributes') }}>
@@ -138,7 +139,7 @@ link for details), create a ``gender_widget`` block to handle this:
138
139
139
140
.. code-block :: html+php
140
141
141
- <!-- app/Resources/views/form/gender_widget .html.php -->
142
+ <!-- app/Resources/views/form/shipping_widget .html.php -->
142
143
<?php if ($expanded) : ?>
143
144
<ul <?php $view['form']->block($form, 'widget_container_attributes') ?>>
144
145
<?php foreach ($form as $child) : ?>
@@ -156,7 +157,7 @@ link for details), create a ``gender_widget`` block to handle this:
156
157
.. note ::
157
158
158
159
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 `).
160
161
Further, the main config file should point to the custom form template
161
162
so that it's used when rendering all forms.
162
163
@@ -253,20 +254,20 @@ new instance of the type in one of your forms::
253
254
254
255
use Symfony\Component\Form\AbstractType;
255
256
use Symfony\Component\Form\FormBuilderInterface;
256
- use AppBundle\Form\Type\GenderType ;
257
+ use AppBundle\Form\Type\ShippingType ;
257
258
258
- class AuthorType extends AbstractType
259
+ class OrderType extends AbstractType
259
260
{
260
261
public function buildForm(FormBuilderInterface $builder, array $options)
261
262
{
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 ',
264
265
));
265
266
}
266
267
}
267
268
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
270
271
section explains how more complex field types solve this problem.
271
272
272
273
.. _form-field-service :
@@ -277,17 +278,18 @@ Creating your Field Type as a Service
277
278
So far, this entry has assumed that you have a very simple custom field type.
278
279
But if you need access to configuration, a database connection, or some other
279
280
service, 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:
281
282
282
283
.. configuration-block ::
283
284
284
285
.. code-block :: yaml
285
286
286
287
# app/config/config.yml
287
288
parameters :
288
- genders :
289
- m : Male
290
- f : Female
289
+ shipping_options :
290
+ standard : Standard Shipping
291
+ expedited : Expedited Shipping
292
+ priority : Priority Shipping
291
293
292
294
.. code-block :: xml
293
295
@@ -299,21 +301,25 @@ example, suppose that you're storing the gender parameters in configuration:
299
301
http://symfony.com/schema/dic/services/services-1.0.xsd" >
300
302
301
303
<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 >
305
308
</parameter >
306
309
</parameters >
307
310
</container >
308
311
309
312
.. code-block :: php
310
313
311
314
// 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
317
323
``__construct() `` function:
318
324
319
325
.. configuration-block ::
@@ -322,10 +328,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
322
328
323
329
# src/AppBundle/Resources/config/services.yml
324
330
services :
325
- app.form.type.gender :
326
- class : AppBundle\Form\Type\GenderType
331
+ app.form.type.shipping :
332
+ class : AppBundle\Form\Type\ShippingType
327
333
arguments :
328
- - ' %genders %'
334
+ - ' %shipping_options %'
329
335
tags :
330
336
- { name: form.type }
331
337
@@ -339,8 +345,8 @@ the ``genders`` parameter value as the first argument to its to-be-created
339
345
http://symfony.com/schema/dic/services/services-1.0.xsd" >
340
346
341
347
<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 >
344
350
<tag name =" form.type" />
345
351
</service >
346
352
</services >
@@ -349,10 +355,10 @@ the ``genders`` parameter value as the first argument to its to-be-created
349
355
.. code-block :: php
350
356
351
357
// src/AppBundle/Resources/config/services.php
352
- use AppBundle\Form\Type\GenderType ;
358
+ use AppBundle\Form\Type\ShippingType ;
353
359
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 %')
356
362
->addTag('form.type')
357
363
;
358
364
@@ -361,54 +367,54 @@ the ``genders`` parameter value as the first argument to its to-be-created
361
367
Make sure the services file is being imported. See :ref: `service-container-imports-directive `
362
368
for details.
363
369
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::
366
372
367
- // src/AppBundle/Form/Type/GenderType .php
373
+ // src/AppBundle/Form/Type/ShippingType .php
368
374
namespace AppBundle\Form\Type;
369
375
370
376
use Symfony\Component\OptionsResolver\OptionsResolver;
371
377
372
378
// ...
373
379
374
380
// ...
375
- class GenderType extends AbstractType
381
+ class ShippingType extends AbstractType
376
382
{
377
- private $genderChoices ;
383
+ private $shippingOptions ;
378
384
379
- public function __construct(array $genderChoices )
385
+ public function __construct(array $shippingOptions )
380
386
{
381
- $this->genderChoices = $genderChoices ;
387
+ $this->shippingOptions = $shippingOptions ;
382
388
}
383
389
384
390
public function configureOptions(OptionsResolver $resolver)
385
391
{
386
392
$resolver->setDefaults(array(
387
- 'choices' => $this->genderChoices ,
393
+ 'choices' => array_flip( $this->shippingOptions) ,
388
394
));
389
395
}
390
396
391
397
// ...
392
398
}
393
399
394
- Great! The ``GenderType `` is now fueled by the configuration parameters and
400
+ Great! The ``ShippingType `` is now fueled by the configuration parameters and
395
401
registered 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,
397
403
your controller *does not need to change *, it still looks like this::
398
404
399
405
// src/AppBundle/Form/Type/AuthorType.php
400
406
namespace AppBundle\Form\Type;
401
407
402
408
use Symfony\Component\Form\AbstractType;
403
409
use Symfony\Component\Form\FormBuilderInterface;
404
- use AppBundle\Form\Type\GenderType ;
410
+ use AppBundle\Form\Type\ShippingType ;
405
411
406
- class AuthorType extends AbstractType
412
+ class OrderType extends AbstractType
407
413
{
408
414
public function buildForm(FormBuilderInterface $builder, array $options)
409
415
{
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 ',
412
418
));
413
419
}
414
420
}
0 commit comments