Skip to content

Commit de00067

Browse files
committed
Merge branch '2.0'
2 parents 4652d2e + cf0c60e commit de00067

File tree

6 files changed

+531
-46
lines changed

6 files changed

+531
-46
lines changed

book/doctrine.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ Relationship Mapping Metadata
768768
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
769769

770770
To relate the ``Category`` and ``Product`` entities, start by creating a
771-
``products`` property on the ``Category`` class::
771+
``products`` property on the ``Category`` class:
772772

773773
.. configuration-block::
774774

@@ -828,7 +828,7 @@ makes sense in the application for each ``Category`` to hold an array of
828828
namespace as the targetEntity.
829829

830830
Next, since each ``Product`` class can relate to exactly one ``Category``
831-
object, you'll want to add a ``$category`` property to the ``Product`` class::
831+
object, you'll want to add a ``$category`` property to the ``Product`` class:
832832

833833
.. configuration-block::
834834

book/service_container.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,7 @@ configuration.
390390
391391
# app/config/config.yml
392392
imports:
393-
hello_bundle:
394-
resource: @AcmeHelloBundle/Resources/config/services.yml
393+
- { resource: @AcmeHelloBundle/Resources/config/services.yml }
395394
396395
.. code-block:: xml
397396

cookbook/form/create_custom_field_type.rst

+274-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,277 @@
44
How to Create a Custom Form Field Type
55
======================================
66

7-
This article has not been written yet, but will soon. If you're interested
8-
in writing this entry, see :doc:`/contributing/documentation/overview`.
7+
Symfony comes with a bunch of core field types available for building forms.
8+
However there are situations where we want to create a custom form field
9+
type for a specific purpose. This recipe assumes we need a field definition
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 for use in our application.
13+
14+
Defining the Field Type
15+
-----------------------
16+
17+
In order to create the custom field type, first we have to create the class
18+
representing the field. In our situation the class holding the field type
19+
will be called `GenderType` and the file will be stored in the default location
20+
for form fields, which is ``<BundleName>\Form\Type``. Make sure the field extends
21+
``AbstractType``::
22+
23+
# src/Acme/DemoBundle/Form/Type/GenderType.php
24+
namespace Acme\DemoBundle\Form\Type;
25+
26+
use Symfony\Component\Form\AbstractType;
27+
use Symfony\Component\Form\FormBuilder;
28+
29+
class GenderType extends AbstractType
30+
{
31+
public function getDefaultOptions(array $options)
32+
{
33+
return array(
34+
'choices' => array(
35+
'm' => 'Male',
36+
'f' => 'Female',
37+
)
38+
);
39+
}
40+
41+
public function getParent(array $options)
42+
{
43+
return 'choice';
44+
}
45+
46+
public function getName()
47+
{
48+
return 'gender';
49+
}
50+
}
51+
52+
.. tip::
53+
54+
The location of this file is not important - the ``Form\Type`` directory
55+
is just a convention.
56+
57+
Here, the return value of the ``getParent`` function indicates that we're
58+
extending the ``choice`` field type. This means that, by default, we inherit
59+
all of the logic and rendering of that field type. To see some of the logic,
60+
check out the `ChoiceType`_ class. There are three methods that are particularly
61+
important:
62+
63+
* ``buildForm()`` - Each field type has a ``buildForm`` method, which is where
64+
you configure and build any field(s). Notice that this is the same method
65+
you use to setup *your* forms, and it works the same here.
66+
67+
* ``buildView()`` - This method is used to set any extra variables you'll
68+
need when rendering your field in a template. For example, in ``ChoiceType``,
69+
a ``multiple`` variable is set and used in the template to set (or not
70+
set) the ``multiple`` attribute on the ``select`` field. See `Creating a Template for the Field`_
71+
for more details.
72+
73+
* ``getDefaultOptions()`` - This defines options for your form type that
74+
can be used in ``buildForm()`` and ``buildView()``. There are a lot of
75+
options common to all fields (see `FieldType`_), but you can create any
76+
others that you need here.
77+
78+
.. tip::
79+
80+
If you're creating a field that consists of many fields, then be sure
81+
to set your "parent" type as ``form`` or something that extends ``form``.
82+
Also, if you need to modify the "view" of any of your child types from
83+
your parent type, use the ``buildViewBottomUp()`` method.
84+
85+
The ``getName()`` method returns an identifier which is used to prevent conflicts
86+
with other types. Other than needing to be unique, this method isn't very
87+
important.
88+
89+
The goal of our field was to extend the choice type to enable selection of
90+
a gender. This is achieved by fixing the ``choices`` to a list of possible
91+
genders.
92+
93+
Creating a Template for the Field
94+
---------------------------------
95+
96+
Each field type is rendered by a template fragment, which is determined in
97+
part by the value of your ``getName()`` method. For more information, see
98+
:ref:`cookbook-form-customization-form-themes`.
99+
100+
In this case, since our parent field is ``choice``, we don't *need* to do
101+
any work as our custom field type will automatically be rendered like a ``choice``
102+
type. But for the sake of this example, let's suppose that when our field
103+
is "expanded" (i.e. radio buttons or checkboxes, instead of a select field),
104+
we want to always render it in a ``ul`` element. In your form theme template
105+
(see above link for details), create a ``gender_widget`` block to handle this:
106+
107+
.. code-block:: html+jinja
108+
109+
{# src/Acme/DemoBundle/Resources/Form/fields.html.twig #}
110+
{% use 'form_div_layout.html.twig' with choice_widget %}
111+
112+
{% block gender_widget %}
113+
{% spaceless %}
114+
{% if expanded %}
115+
<ul {{ block('widget_container_attributes') }}>
116+
{% for child in form %}
117+
<li>
118+
{{ form_widget(child) }}
119+
{{ form_label(child) }}
120+
</li>
121+
{% endfor %}
122+
</ul>
123+
{% else %}
124+
{# just let the choice widget render the select tag #}
125+
{{ block('choice_widget') }}
126+
{% endif %}
127+
{% endspaceless %}
128+
{% endblock %}
129+
130+
.. note::
131+
132+
Make sure the correct widget prefix is used. In this example the name should
133+
be ``gender_widget``, according to the value returned by ``getName``.
134+
Further, the main config file should point to the custom form template
135+
so that it's used when rendering all forms.
136+
137+
.. code-block:: yaml
138+
139+
# app/config/config.yml
140+
141+
twig:
142+
form:
143+
resources:
144+
- 'AcmeDemoBundle:Form:fields.html.twig'
145+
146+
Using the Field Type
147+
--------------------
148+
149+
You can now use your custom field type immediately, simply by creating a
150+
new instance of the type in one of your forms::
151+
152+
// src/Acme/DemoBundle/Form/Type/AuthorType.php
153+
namespace Acme\DemoBundle\Form\Type;
154+
155+
use Symfony\Component\Form\AbstractType;
156+
use Symfony\Component\Form\FormBuilder;
157+
158+
class AuthorType extends AbstractType
159+
{
160+
public function buildForm(FormBuilder $builder, array $options)
161+
{
162+
$builder->add('gender_code', new GenderType(), array(
163+
'empty_value' => 'Choose a gender',
164+
));
165+
}
166+
}
167+
168+
But this only works because the ``GenderType()`` is very simple. What if
169+
the gender codes were stored in configuration or in a database? The next
170+
section explains how more complex field types solve this problem.
171+
172+
Creating your Field Type as a Service
173+
-------------------------------------
174+
175+
So far, this entry has assumed that you have a very simple custom field type.
176+
But if you need access to configuration, a database connection, or some other
177+
service, then you'll want to register your custom type as a service. For
178+
example, suppose that we're storing the gender parameters in configuration:
179+
180+
.. configuration-block::
181+
182+
.. code-block:: yaml
183+
184+
# app/config/config.yml
185+
parameters:
186+
genders:
187+
m: Male
188+
f: Female
189+
190+
.. code-block:: xml
191+
192+
<!-- app/config/config.xml -->
193+
<parameters>
194+
<parameter key="genders" type="collection">
195+
<parameter key="m">Male</parameter>
196+
<parameter key="f">Female</parameter>
197+
</parameter>
198+
</parameters>
199+
200+
To use the parameter, we'll define our custom field type as a service, injecting
201+
the ``genders`` parameter value as the first argument to its to-be-created
202+
``__construct`` function:
203+
204+
.. configuration-block::
205+
206+
.. code-block:: yaml
207+
208+
# src/Acme/DemoBundle/Resources/config/services.yml
209+
services:
210+
form.type.gender:
211+
class: Acme\DemoBundle\Form\Type\GenderType
212+
arguments:
213+
- "%genders%"
214+
tags:
215+
- { name: form.type, alias: gender }
216+
217+
.. code-block:: xml
218+
219+
<!-- src/Acme/DemoBundle/Resources/config/services.xml -->
220+
<service id="form.type.gender" class="Acme\DemoBundle\Form\Type\GenderType">
221+
<argument>%genders%</argument>
222+
<tag name="form.type" alias="gender" />
223+
</service>
224+
225+
.. tip::
226+
227+
Make sure the services file is being imported. See :ref:`service-container-imports-directive`
228+
for details.
229+
230+
Be sure that the ``alias`` tag corresponds with the value returned by the
231+
``getName`` method defined earlier. We'll see the importance of this in a
232+
moment when we use the custom field type. But first, add a ``__construct``
233+
argument to ``GenderType``, which receives the gender configuration::
234+
235+
# src/Acme/DemoBundle/Form/Type/GenderType.php
236+
namespace Acme\DemoBundle\Form\Type;
237+
// ...
238+
239+
class GenderType extends AbstractType
240+
{
241+
private $genderChoices;
242+
243+
public function __construct(array $genderChoices)
244+
{
245+
$this->genderChoices = $genderChoices;
246+
}
247+
248+
public function getDefaultOptions(array $options)
249+
{
250+
return array(
251+
'choices' => $this->genderChoices;
252+
);
253+
}
254+
255+
// ...
256+
}
257+
258+
Great! The ``GenderType`` is now fueled by the configuration parameters and
259+
registered as a service. And because we used the ``form.type`` alias in its
260+
configuration, using the field is now much easier::
261+
262+
// src/Acme/DemoBundle/Form/Type/AuthorType.php
263+
namespace Acme\DemoBundle\Form\Type;
264+
// ...
265+
266+
class AuthorType extends AbstractType
267+
{
268+
public function buildForm(FormBuilder $builder, array $options)
269+
{
270+
$builder->add('gender_code', 'gender', array(
271+
'empty_value' => 'Choose a gender',
272+
));
273+
}
274+
}
275+
276+
Notice that instead of instantiating a new instance, we can just refer to
277+
it by the alias used in our service configuration, ``gender``. Have fun!
278+
279+
.. _`ChoiceType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
280+
.. _`FieldType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php

cookbook/form/form_customization.rst

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ The remainder of this recipe will explain how every part of the form's markup
7777
can be modified at several different levels. For more information about form
7878
rendering in general, see :ref:`form-rendering-template`.
7979

80+
.. _cookbook-form-customization-form-themes:
81+
8082
What are Form Themes?
8183
---------------------
8284

0 commit comments

Comments
 (0)