summaryrefslogtreecommitdiff
path: root/UPGRADE-2.3.md
blob: 3b191fae95b06388b7878eb9fb38064895dca131 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
UPGRADE FROM 2.2 to 2.3
=======================

Form
----

 * Although this was not officially supported nor documented, it was possible to
   set the option "validation_groups" to false, resulting in the group "Default"
   being validated. Now, if you set "validation_groups" to false, the validation
   of a form will be skipped (except for a few integrity checks on the form).

   If you want to validate a form in group "Default", you should either
   explicitly set "validation_groups" to "Default" or alternatively set it to
   null.

   Before:

   ```
   // equivalent notations for validating in group "Default"
   "validation_groups" => null
   "validation_groups" => "Default"
   "validation_groups" => false

   // notation for skipping validation
   "validation_groups" => array()
   ```

   After:

   ```
   // equivalent notations for validating in group "Default"
   "validation_groups" => null
   "validation_groups" => "Default"

   // equivalent notations for skipping validation
   "validation_groups" => false
   "validation_groups" => array()
   ```
 * The array type hint from DataMapperInterface was removed. You should adapt
   implementations of that interface accordingly.

   Before:

   ```php
   use Symfony\Component\Form\DataMapperInterface;

   class MyDataMapper
   {
       public function mapFormsToData(array $forms, $data)
       {
           // ...
       }

       public function mapDataToForms($data, array $forms)
       {
           // ...
       }
   }
   ```

   After:

   ```php
   use Symfony\Component\Form\DataMapperInterface;

   class MyDataMapper
   {
       public function mapFormsToData($forms, $data)
       {
           // ...
       }

       public function mapDataToForms($data, $forms)
       {
           // ...
       }
   }
   ```

   Instead of an array, the methods here are now passed a
   RecursiveIteratorIterator containing an InheritDataAwareIterator by default,
   so you don't need to handle forms inheriting their parent data (former
   "virtual forms") in the data mapper anymore.

   Before:

   ```php
   use Symfony\Component\Form\Util\VirtualFormAwareIterator;

   public function mapFormsToData(array $forms, $data)
   {
       $iterator = new \RecursiveIteratorIterator(
           new VirtualFormAwareIterator($forms)
       );

       foreach ($iterator as $form) {
           // ...
       }
   }
   ```

   After:

   ```php
   public function mapFormsToData($forms, $data)
   {
       foreach ($forms as $form) {
           // ...
       }
   }
   ```

 * The `*_SET_DATA` events are now guaranteed to be fired *after* the children
   were added by the FormBuilder (unless setData() is called manually). Before,
   the `*_SET_DATA` events were sometimes thrown before adding child forms,
   which made it impossible to remove child forms dynamically.

   A consequence of this change is that you need to set the "auto_initialize"
   option to `false` for `FormInterface` instances that you pass to
   `FormInterface::add()`:

   Before:

   ```php
   $form = $factory->create('form');
   $form->add($factory->createNamed('field', 'text'));
   ```

   This code will now throw an exception with the following message:

   Automatic initialization is only supported on root forms. You should set the
   "auto_initialize" option to false on the field "field".

   Consequently, you need to set the "auto_initialize" option:

   After (Alternative 1):

   ```php
   $form = $factory->create('form');
   $form->add($factory->createNamed('field', 'text', array(), array(
       'auto_initialize' => false,
   )));
   ```

   The problem also disappears if you work with `FormBuilder` instances instead
   of `Form` instances:

   After (Alternative 2):

   ```php
   $builder = $factory->createBuilder('form');
   $builder->add($factory->createBuilder('field', 'text'));
   $form = $builder->getForm();
   ```

   The best solution is in most cases to let `add()` handle the field creation:

   After (Alternative 3):

   ```php
   $form = $factory->create('form');
   $form->add('field', 'text');
   ```

   After (Alternative 4):

   ```php
   $builder = $factory->createBuilder('form');
   $builder->add('field', 'text');
   $form = $builder->getForm();
   ```

 * Previously, when the "data" option of a field was set to `null` and the
   containing form was mapped to an object, the field would receive the data
   from the object as default value. This functionality was unintended and fixed
   to use `null` as default value in Symfony 2.3.

   In cases where you made use of the previous behavior, you should now remove
   the "data" option altogether.

   Before:

   ```php
   $builder->add('field', 'text', array(
      'data' => $defaultData ?: null,
   ));
   ```

   After:

   ```php
   $options = array();
   if ($defaultData) {
       $options['data'] = $defaultData;
   }
   $builder->add('field', 'text', $options);
   ```

PropertyAccess
--------------

 * PropertyAccessor was changed to continue its search for a property or method
   even if a non-public match was found. This means that the property "author"
   in the following class will now correctly be found:

   ```php
   class Article
   {
       public $author;

       private function getAuthor()
       {
           // ...
       }
   }
   ```

   Although this is uncommon, similar cases exist in practice.

   Instead of the PropertyAccessDeniedException that was thrown here, the more
   generic NoSuchPropertyException is thrown now if no public property nor
   method are found by the PropertyAccessor. PropertyAccessDeniedException was
   removed completely.

   Before:

   ```php
   use Symfony\Component\PropertyAccess\Exception\PropertyAccessDeniedException;
   use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;

   try {
       $value = $accessor->getValue($article, 'author');
   } catch (PropertyAccessDeniedException $e) {
       // Method/property was found but not public
   } catch (NoSuchPropertyException $e) {
       // Method/property was not found
   }
   ```

   After:

   ```php
   use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;

   try {
       $value = $accessor->getValue($article, 'author');
   } catch (NoSuchPropertyException $e) {
       // Method/property was not found or not public
   }
   ```

DomCrawler
----------

 * `Crawler::each()` and `Crawler::reduce()` now return Crawler instances
   instead of DomElement instances:

   Before:

   ```php
   $data = $crawler->each(function ($node, $i) {
       return $node->nodeValue;
   });
   ```

   After:

   ```php
   $data = $crawler->each(function ($crawler, $i) {
       return $crawler->text();
   });
   ```

Console
-------

 * New verbosity levels have been added, therefore if you used to do check
   the output verbosity level directly for VERBOSITY_VERBOSE you probably
   want to update it to a greater than comparison:

   Before:

   ```php
   if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { ... }
   ```

   After:

   ```php
   if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { ... }
   ```

BrowserKit
----------

 * If you are receiving responses with non-3xx Status Code and Location header
   please be aware that you won't be able to use auto-redirects on these kind
   of responses.

   If you are correctly passing 3xx Status Code with Location header, you
   don't have to worry about the change.

   If you were using responses with Location header and non-3xx Status Code,
   you have to update your code to manually create another request to URL
   grabbed from the Location header.