|
| 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