Skip to content

Commit fd8f013

Browse files
committed
Merge pull request #2353 from WouterJ/document_optionresolver
[WIP] Bootstrapped Options Resolver Component
2 parents 33ffa80 + b1a5bbc commit fd8f013

File tree

3 files changed

+307
-0
lines changed

3 files changed

+307
-0
lines changed

components/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The Components
1717
http_foundation/index
1818
http_kernel/index
1919
locale
20+
options_resolver
2021
process
2122
routing/index
2223
security/index

components/map.rst.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070

7171
* :doc:`/components/locale`
7272

73+
* **Options Resolver**
74+
75+
* :doc:`/components/options_resolver`
76+
7377
* **Process**
7478

7579
* :doc:`/components/process`

components/options_resolver.rst

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
.. index::
2+
single: Options Resolver
3+
single: Components; OptionsResolver
4+
5+
The OptionsResolver Component
6+
=============================
7+
8+
The OptionsResolver Component helps you at configuring objects with option
9+
arrays. It supports default values, option constraints and lazy options.
10+
11+
.. versionadded:: 2.1
12+
The OptionsResolver Component is new in Symfony2.1
13+
14+
Installation
15+
------------
16+
17+
You can install the component in several different ways:
18+
19+
* Use the official Git repository (https://github.com/symfony/OptionsResolver
20+
* :doc:`Install it via Composer</components/using_components>` (``symfony/options-resolver`` on `Packagist`_)
21+
22+
Usage
23+
-----
24+
25+
Imagine you have a ``Person`` class which has 2 options: ``firstName`` and
26+
``lastName``. These options are going to be handled by the OptionsResolver
27+
Component.
28+
29+
First of all, you should create some basic skeleton::
30+
31+
class Person
32+
{
33+
protected $options;
34+
35+
public function __construct(array $options = array())
36+
{
37+
}
38+
}
39+
40+
Now, you should handle the ``$options`` parameter with the
41+
:class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class. To do
42+
this, you should instantiate the ``OptionsResolver`` class and let it resolve
43+
the options by calling
44+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::resolve`::
45+
46+
use Symfony\Component\OptionsResolver\OptionsResolver;
47+
48+
// ...
49+
public function __construct(array $options = array())
50+
{
51+
$resolver = new OptionsResolver();
52+
53+
$this->options = $resolver->resolve($options);
54+
}
55+
56+
The ``$options`` property is an instance of
57+
:class:`Symfony\\Component\\OptionsResolver\\Options`, which implements
58+
:phpclass:`ArrayAccess`, :phpclass:`Iterator` and :phpclass:`Countable`. That
59+
means you can handle it as a normal array::
60+
61+
// ...
62+
public function getFirstName()
63+
{
64+
return $this->options['firstName'];
65+
}
66+
67+
public function getFullName()
68+
{
69+
$name = $this->options['firstName'];
70+
71+
if (isset($this->options['lastName'])) {
72+
$name .= ' '.$this->options['lastName'];
73+
}
74+
75+
return $name;
76+
}
77+
78+
Let's use the class::
79+
80+
$person = new Person(array(
81+
'firstName' => 'Wouter',
82+
'lastName' => 'de Jong',
83+
));
84+
85+
echo $person->getFirstName();
86+
87+
As you see, you get a
88+
:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException`
89+
which tells you that the options ``firstName`` and ``lastName`` not exists.
90+
You need to configure the ``OptionsResolver`` first, so it knows which options
91+
should be resolved.
92+
93+
.. tip::
94+
95+
To check if an option exists, you can use the
96+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isKnown` isser.
97+
98+
A best practise is to put the configuration in a method (e.g.
99+
``setDefaultOptions``). You call this method in the constructor to configure
100+
the ``OptionsResolver`` class::
101+
102+
use Symfony\Component\OptionsResolver\OptionsResolver;
103+
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
104+
105+
class Person
106+
{
107+
protected $options;
108+
109+
public function __construct(array $options = array())
110+
{
111+
$resolver = new OptionsResolver();
112+
$this->setDefaultOptions($resolver);
113+
114+
$this->options = $resolver->resolve($options);
115+
}
116+
117+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
118+
{
119+
// ... configure the resolver, you will learn this in the sections below
120+
}
121+
}
122+
123+
Required Options
124+
----------------
125+
126+
The ``firstName`` option is required; the class can't work without that
127+
option. You can set the required options by calling
128+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setRequired`::
129+
130+
// ...
131+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
132+
{
133+
$resolver->setRequired(array('firstName'));
134+
}
135+
136+
You are now able to use the class without errors::
137+
138+
$person = new Person(array(
139+
'firstName' => 'Wouter',
140+
));
141+
142+
echo $person->getFirstName(); // 'Wouter'
143+
144+
If you don't pass a required option, an
145+
:class:`Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException`
146+
will be thrown.
147+
148+
To determine if an option is required, you can use the
149+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired`
150+
method.
151+
152+
Optional Options
153+
----------------
154+
155+
Sometimes, an option can be optional (e.g. the ``lastName`` option in the
156+
``Person`` class). You can configure these options by calling
157+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptional`::
158+
159+
// ...
160+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
161+
{
162+
// ...
163+
164+
$resolver->setOptional(array('lastName'));
165+
}
166+
167+
Set Default Values
168+
------------------
169+
170+
Most of the optional options have a default value. You can configure these
171+
options by calling
172+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefaults`::
173+
174+
// ...
175+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
176+
{
177+
// ...
178+
179+
$resolver->setDefaults(array(
180+
'age' => 0,
181+
));
182+
}
183+
184+
The default age will be ``0`` now. When the user specifies an age, it gets
185+
replaced. You don't need to configure ``age`` as an optional option. The
186+
``OptionsResolver`` already knows that options with a default value are
187+
optional.
188+
189+
The ``OptionsResolver`` component also has an
190+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::replaceDefaults`
191+
method. This can be used to override the previous default value. The closure
192+
that is passed has 2 parameters:
193+
194+
* ``$options`` (an :class:`Symfony\\Component\\OptionsResolver\\Options`
195+
instance), with all the default options
196+
* ``$value``, the previous set default value
197+
198+
Default values that depend on another option
199+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
200+
201+
If you add a ``gender`` option to the ``Person`` class, it should get a
202+
default value which guess the gender based on the first name. You can do that
203+
easily by using a Closure as default value::
204+
205+
use Symfony\Component\OptionsResolver\Options;
206+
207+
// ...
208+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
209+
{
210+
// ...
211+
212+
$resolver->setDefaults(array(
213+
'gender' => function (Options $options) {
214+
if (GenderGuesser::isMale($options['firstName'])) {
215+
return 'male';
216+
}
217+
218+
return 'female';
219+
},
220+
));
221+
}
222+
223+
.. caution::
224+
225+
The first argument of the Closure must be typehinted as `Options`,
226+
otherwise it is considered as the value.
227+
228+
Configure allowed values
229+
------------------------
230+
231+
Not all values are valid values for options. For instance, the ``gender``
232+
option can only be ``female`` or ``male``. You can configure these allowed
233+
values by calling
234+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedValues`::
235+
236+
// ...
237+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
238+
{
239+
// ...
240+
241+
$resolver->setAllowedValues(array(
242+
'gender' => array('male', 'female'),
243+
));
244+
}
245+
246+
There is also a
247+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedValues`
248+
method, which you can use if you want to add an allowed value to the previous
249+
setted allowed values.
250+
251+
Configure allowed Types
252+
~~~~~~~~~~~~~~~~~~~~~~~
253+
254+
You can also specify allowed types. For instance, the ``firstName`` option can
255+
be anything, but it must be a string. You can configure these types by calling
256+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedTypes`::
257+
258+
// ...
259+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
260+
{
261+
// ...
262+
263+
$resolver->setAllowedTypes(array(
264+
'firstName' => 'string',
265+
));
266+
}
267+
268+
Possible types are the one associated with the ``is_*`` php functions or a
269+
class name. You can also pass an array of types as value. For instance,
270+
``array('null', 'string')`` allows ``firstName`` to be ``null`` or a
271+
``string``.
272+
273+
There is also a
274+
:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedTypes`
275+
method, which you can use to add an allowed type to the previous allowed types.
276+
277+
Normalize the Options
278+
---------------------
279+
280+
Some values needs to be normalized before you can use them. For instance, the
281+
``firstName`` should always start with an uppercase letter. To do that, you can
282+
write normalizers. These Closures will be executed after all options are
283+
passed and return the normalized value. You can configure these normalizers by
284+
calling
285+
:method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizers`::
286+
287+
// ...
288+
protected function setDefaultOptions(OptionsResolverInterface $resolver)
289+
{
290+
// ...
291+
292+
$resolver->setNormalizers(array(
293+
'firstName' => function (Options $options, $value) {
294+
return ucfirst($value);
295+
},
296+
));
297+
}
298+
299+
You see that the closure also get an ``$options`` parameter. Sometimes, you
300+
need to use the other options for normalizing.
301+
302+
.. _Packagist: https://packagist.org/packages/symfony/options-resolver

0 commit comments

Comments
 (0)