Skip to content
/ form Public

Commit 896c5f2

Browse files
HeahDudexabbuh
authored andcommittedOct 11, 2024
[Form] Use form.post_set_data in ResizeFormListener
1 parent e3fe767 commit 896c5f2

File tree

4 files changed

+233
-114
lines changed

4 files changed

+233
-114
lines changed
 

‎CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ CHANGELOG
77
* Deprecate the `VersionAwareTest` trait, use feature detection instead
88
* Add support for the `calendar` option in `DateType`
99
* Add `LazyChoiceLoader` and `choice_lazy` option in `ChoiceType` for loading and rendering choices on demand
10+
* Use `form.post_set_data` instead of `form.pre_set_data` in `ResizeFormListener`
11+
* Change the priority of `DataCollectorListener` from 255 to -255
1012

1113
7.1
1214
---

‎Extension/Core/EventListener/ResizeFormListener.php

+47-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Extension\Core\EventListener;
1313

1414
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15+
use Symfony\Component\Form\Event\PostSetDataEvent;
1516
use Symfony\Component\Form\Exception\UnexpectedTypeException;
1617
use Symfony\Component\Form\FormEvent;
1718
use Symfony\Component\Form\FormEvents;
@@ -27,6 +28,9 @@ class ResizeFormListener implements EventSubscriberInterface
2728
protected array $prototypeOptions;
2829

2930
private \Closure|bool $deleteEmpty;
31+
// BC, to be removed in 8.0
32+
private bool $overridden = true;
33+
private bool $usePreSetData = false;
3034

3135
public function __construct(
3236
private string $type,
@@ -44,15 +48,57 @@ public function __construct(
4448
public static function getSubscribedEvents(): array
4549
{
4650
return [
47-
FormEvents::PRE_SET_DATA => 'preSetData',
51+
FormEvents::PRE_SET_DATA => 'preSetData', // deprecated
52+
FormEvents::POST_SET_DATA => ['postSetData', 255], // as early as possible
4853
FormEvents::PRE_SUBMIT => 'preSubmit',
4954
// (MergeCollectionListener, MergeDoctrineCollectionListener)
5055
FormEvents::SUBMIT => ['onSubmit', 50],
5156
];
5257
}
5358

59+
/**
60+
* @deprecated Since Symfony 7.2, use {@see postSetData()} instead.
61+
*/
5462
public function preSetData(FormEvent $event): void
5563
{
64+
if (__CLASS__ === static::class
65+
|| __CLASS__ === (new \ReflectionClass($this))->getMethod('preSetData')->getDeclaringClass()->name
66+
) {
67+
// not a child class, or child class does not overload PRE_SET_DATA
68+
return;
69+
}
70+
71+
trigger_deprecation('symfony/form', '7.2', 'Calling "%s()" is deprecated, use "%s::postSetData()" instead.', __METHOD__, __CLASS__);
72+
// parent::preSetData() has been called
73+
$this->overridden = false;
74+
try {
75+
$this->postSetData($event);
76+
} finally {
77+
$this->usePreSetData = true;
78+
}
79+
}
80+
81+
/**
82+
* Remove FormEvent type hint in 8.0.
83+
*
84+
* @final since Symfony 7.2
85+
*/
86+
public function postSetData(FormEvent|PostSetDataEvent $event): void
87+
{
88+
if (__CLASS__ !== static::class) {
89+
if ($this->overridden) {
90+
trigger_deprecation('symfony/form', '7.2', 'Calling "%s::preSetData()" is deprecated, use "%s::postSetData()" instead.', static::class, __CLASS__);
91+
// parent::preSetData() has not been called, noop
92+
93+
return;
94+
}
95+
96+
if ($this->usePreSetData) {
97+
// nothing else to do
98+
return;
99+
}
100+
}
101+
56102
$form = $event->getForm();
57103
$data = $event->getData() ?? [];
58104

‎Extension/DataCollector/EventListener/DataCollectorListener.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public function __construct(
3232
public static function getSubscribedEvents(): array
3333
{
3434
return [
35-
// High priority in order to be called as soon as possible
36-
FormEvents::POST_SET_DATA => ['postSetData', 255],
35+
// Low priority in order to be called as late as possible
36+
FormEvents::POST_SET_DATA => ['postSetData', -255],
3737
// Low priority in order to be called as late as possible
3838
FormEvents::POST_SUBMIT => ['postSubmit', -255],
3939
];

‎Tests/Extension/Core/EventListener/ResizeFormListenerTest.php

+182-111
Original file line numberDiff line numberDiff line change
@@ -14,183 +14,263 @@
1414
use Doctrine\Common\Collections\ArrayCollection;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\EventDispatcher\EventDispatcher;
17+
use Symfony\Component\Form\AbstractType;
1718
use Symfony\Component\Form\Exception\UnexpectedTypeException;
1819
use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
1920
use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener;
2021
use Symfony\Component\Form\Extension\Core\Type\TextType;
2122
use Symfony\Component\Form\FormBuilder;
23+
use Symfony\Component\Form\FormBuilderInterface;
2224
use Symfony\Component\Form\FormEvent;
2325
use Symfony\Component\Form\FormFactoryBuilder;
2426
use Symfony\Component\Form\FormFactoryInterface;
25-
use Symfony\Component\Form\FormInterface;
2627

2728
class ResizeFormListenerTest extends TestCase
2829
{
2930
private FormFactoryInterface $factory;
30-
private FormInterface $form;
31+
private FormBuilderInterface $builder;
3132

3233
protected function setUp(): void
3334
{
3435
$this->factory = (new FormFactoryBuilder())->getFormFactory();
35-
$this->form = $this->getBuilder()
36+
$this->builder = $this->getBuilder()
3637
->setCompound(true)
37-
->setDataMapper(new DataMapper())
38-
->getForm();
38+
->setDataMapper(new DataMapper());
3939
}
4040

4141
protected function getBuilder($name = 'name')
4242
{
4343
return new FormBuilder($name, null, new EventDispatcher(), $this->factory);
4444
}
4545

46-
protected function getForm($name = 'name')
46+
/**
47+
* @group legacy
48+
*/
49+
public function testPreSetDataResizesForm()
4750
{
48-
return $this->getBuilder($name)->getForm();
51+
$this->builder->add($this->getBuilder('0'));
52+
$this->builder->add($this->getBuilder('1'));
53+
$this->builder->addEventSubscriber(new class(TextType::class, ['attr' => ['maxlength' => 10]], false, false) extends ResizeFormListener {
54+
public function preSetData(FormEvent $event): void
55+
{
56+
parent::preSetData($event);
57+
}
58+
});
59+
60+
$form = $this->builder->getForm();
61+
62+
$this->assertTrue($form->has('0'));
63+
64+
// initialize the form
65+
$form->setData([1 => 'string', 2 => 'string']);
66+
67+
$this->assertFalse($form->has('0'));
68+
$this->assertTrue($form->has('1'));
69+
$this->assertTrue($form->has('2'));
70+
71+
$this->assertSame('string', $form->get('1')->getData());
72+
$this->assertSame('string', $form->get('2')->getData());
4973
}
5074

51-
public function testPreSetDataResizesForm()
75+
public function testPostSetDataResizesForm()
5276
{
53-
$this->form->add($this->getForm('0'));
54-
$this->form->add($this->getForm('1'));
77+
$this->builder->add($this->getBuilder('0'));
78+
$this->builder->add($this->getBuilder('1'));
79+
$this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], false, false));
5580

56-
$data = [1 => 'string', 2 => 'string'];
57-
$event = new FormEvent($this->form, $data);
58-
$listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], false, false);
59-
$listener->preSetData($event);
81+
$form = $this->builder->getForm();
82+
83+
$this->assertTrue($form->has('0'));
6084

61-
$this->assertFalse($this->form->has('0'));
62-
$this->assertTrue($this->form->has('1'));
63-
$this->assertTrue($this->form->has('2'));
85+
// initialize the form
86+
$form->setData([1 => 'string', 2 => 'string']);
87+
88+
$this->assertFalse($form->has('0'));
89+
$this->assertTrue($form->has('1'));
90+
$this->assertTrue($form->has('2'));
91+
92+
$this->assertSame('string', $form->get('1')->getData());
93+
$this->assertSame('string', $form->get('2')->getData());
6494
}
6595

96+
/**
97+
* @group legacy
98+
*/
6699
public function testPreSetDataRequiresArrayOrTraversable()
67100
{
68101
$this->expectException(UnexpectedTypeException::class);
69102
$data = 'no array or traversable';
70-
$event = new FormEvent($this->form, $data);
71-
$listener = new ResizeFormListener('text', [], false, false);
103+
$event = new FormEvent($this->builder->getForm(), $data);
104+
$listener = new class(TextType::class, [], false, false) extends ResizeFormListener {
105+
public function preSetData(FormEvent $event): void
106+
{
107+
parent::preSetData($event);
108+
}
109+
};
72110
$listener->preSetData($event);
73111
}
74112

113+
public function testPostSetDataRequiresArrayOrTraversable()
114+
{
115+
$this->expectException(UnexpectedTypeException::class);
116+
$data = 'no array or traversable';
117+
$event = new FormEvent($this->builder->getForm(), $data);
118+
$listener = new ResizeFormListener(TextType::class, [], false, false);
119+
$listener->postSetData($event);
120+
}
121+
122+
/**
123+
* @group legacy
124+
*/
75125
public function testPreSetDataDealsWithNullData()
76126
{
77127
$data = null;
78-
$event = new FormEvent($this->form, $data);
79-
$listener = new ResizeFormListener(TextType::class, [], false, false);
128+
$event = new FormEvent($this->builder->getForm(), $data);
129+
$listener = new class(TextType::class, [], false, false) extends ResizeFormListener {
130+
public function preSetData(FormEvent $event): void
131+
{
132+
parent::preSetData($event);
133+
}
134+
};
80135
$listener->preSetData($event);
81136

82-
$this->assertSame(0, $this->form->count());
137+
$this->assertSame(0, $this->builder->count());
138+
}
139+
140+
public function testPostSetDataDealsWithNullData()
141+
{
142+
$data = null;
143+
$event = new FormEvent($this->builder->getForm(), $data);
144+
$listener = new ResizeFormListener(TextType::class, [], false, false);
145+
$listener->postSetData($event);
146+
147+
$this->assertSame(0, $this->builder->count());
83148
}
84149

85150
public function testPreSubmitResizesUpIfAllowAdd()
86151
{
87-
$this->form->add($this->getForm('0'));
152+
$this->builder->add($this->getBuilder('0'));
153+
$this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], true, false));
88154

89-
$data = [0 => 'string', 1 => 'string'];
90-
$event = new FormEvent($this->form, $data);
91-
$listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], true, false);
92-
$listener->preSubmit($event);
155+
$form = $this->builder->getForm();
156+
157+
$this->assertTrue($form->has('0'));
158+
$this->assertFalse($form->has('1'));
93159

94-
$this->assertTrue($this->form->has('0'));
95-
$this->assertTrue($this->form->has('1'));
160+
$form->submit([0 => 'string', 1 => 'string']);
161+
162+
$this->assertTrue($form->has('0'));
163+
$this->assertTrue($form->has('1'));
96164
}
97165

98166
public function testPreSubmitResizesDownIfAllowDelete()
99167
{
100-
$this->form->add($this->getForm('0'));
101-
$this->form->add($this->getForm('1'));
168+
$this->builder->add($this->getBuilder('0'));
169+
$this->builder->add($this->getBuilder('1'));
170+
$this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, [], false, true));
102171

103-
$data = [0 => 'string'];
104-
$event = new FormEvent($this->form, $data);
105-
$listener = new ResizeFormListener('text', [], false, true);
106-
$listener->preSubmit($event);
172+
$form = $this->builder->getForm();
173+
// initialize the form
174+
$form->setData([0 => 'string', 1 => 'string']);
175+
176+
$this->assertTrue($form->has('0'));
177+
$this->assertTrue($form->has('1'));
107178

108-
$this->assertTrue($this->form->has('0'));
109-
$this->assertFalse($this->form->has('1'));
179+
$form->submit([0 => 'string']);
180+
181+
$this->assertTrue($form->has('0'));
182+
$this->assertFalse($form->has('1'));
110183
}
111184

112185
// fix for https://github.com/symfony/symfony/pull/493
113186
public function testPreSubmitRemovesZeroKeys()
114187
{
115-
$this->form->add($this->getForm('0'));
188+
$this->builder->add($this->getBuilder('0'));
116189

117190
$data = [];
118-
$event = new FormEvent($this->form, $data);
119-
$listener = new ResizeFormListener('text', [], false, true);
191+
$form = $this->builder->getForm();
192+
$event = new FormEvent($form, $data);
193+
$listener = new ResizeFormListener(TextType::class, [], false, true);
120194
$listener->preSubmit($event);
121195

122-
$this->assertFalse($this->form->has('0'));
196+
$this->assertFalse($form->has('0'));
123197
}
124198

125199
public function testPreSubmitDoesNothingIfNotAllowAddNorAllowDelete()
126200
{
127-
$this->form->add($this->getForm('0'));
128-
$this->form->add($this->getForm('1'));
201+
$this->builder->add($this->getBuilder('0'));
202+
$this->builder->add($this->getBuilder('1'));
129203

130204
$data = [0 => 'string', 2 => 'string'];
131-
$event = new FormEvent($this->form, $data);
132-
$listener = new ResizeFormListener('text', [], false, false);
205+
$form = $this->builder->getForm();
206+
$event = new FormEvent($form, $data);
207+
$listener = new ResizeFormListener(TextType::class, [], false, false);
133208
$listener->preSubmit($event);
134209

135-
$this->assertTrue($this->form->has('0'));
136-
$this->assertTrue($this->form->has('1'));
137-
$this->assertFalse($this->form->has('2'));
210+
$this->assertTrue($form->has('0'));
211+
$this->assertTrue($form->has('1'));
212+
$this->assertFalse($form->has('2'));
138213
}
139214

140215
public function testPreSubmitDealsWithNoArrayOrTraversable()
141216
{
142217
$data = 'no array or traversable';
143-
$event = new FormEvent($this->form, $data);
144-
$listener = new ResizeFormListener('text', [], false, false);
218+
$form = $this->builder->getForm();
219+
$event = new FormEvent($form, $data);
220+
$listener = new ResizeFormListener(TextType::class, [], false, false);
145221
$listener->preSubmit($event);
146222

147-
$this->assertFalse($this->form->has('1'));
223+
$this->assertFalse($form->has('1'));
148224
}
149225

150226
public function testPreSubmitDealsWithNullData()
151227
{
152-
$this->form->add($this->getForm('1'));
228+
$this->builder->add($this->getBuilder('1'));
153229

154230
$data = null;
155-
$event = new FormEvent($this->form, $data);
156-
$listener = new ResizeFormListener('text', [], false, true);
231+
$form = $this->builder->getForm();
232+
$event = new FormEvent($form, $data);
233+
$listener = new ResizeFormListener(TextType::class, [], false, true);
157234
$listener->preSubmit($event);
158235

159-
$this->assertFalse($this->form->has('1'));
236+
$this->assertFalse($form->has('1'));
160237
}
161238

162239
// fixes https://github.com/symfony/symfony/pull/40
163240
public function testPreSubmitDealsWithEmptyData()
164241
{
165-
$this->form->add($this->getForm('1'));
242+
$this->builder->add($this->getBuilder('1'));
166243

167244
$data = '';
168-
$event = new FormEvent($this->form, $data);
169-
$listener = new ResizeFormListener('text', [], false, true);
245+
$form = $this->builder->getForm();
246+
$event = new FormEvent($form, $data);
247+
$listener = new ResizeFormListener(TextType::class, [], false, true);
170248
$listener->preSubmit($event);
171249

172-
$this->assertFalse($this->form->has('1'));
250+
$this->assertFalse($form->has('1'));
173251
}
174252

175253
public function testOnSubmitNormDataRemovesEntriesMissingInTheFormIfAllowDelete()
176254
{
177-
$this->form->add($this->getForm('1'));
255+
$this->builder->add($this->getBuilder('1'));
178256

179257
$data = [0 => 'first', 1 => 'second', 2 => 'third'];
180-
$event = new FormEvent($this->form, $data);
181-
$listener = new ResizeFormListener('text', [], false, true);
258+
$form = $this->builder->getForm();
259+
$event = new FormEvent($form, $data);
260+
$listener = new ResizeFormListener(TextType::class, [], false, true);
182261
$listener->onSubmit($event);
183262

184263
$this->assertEquals([1 => 'second'], $event->getData());
185264
}
186265

187266
public function testOnSubmitNormDataDoesNothingIfNotAllowDelete()
188267
{
189-
$this->form->add($this->getForm('1'));
268+
$this->builder->add($this->getBuilder('1'));
190269

191270
$data = [0 => 'first', 1 => 'second', 2 => 'third'];
192-
$event = new FormEvent($this->form, $data);
193-
$listener = new ResizeFormListener('text', [], false, false);
271+
$form = $this->builder->getForm();
272+
$event = new FormEvent($form, $data);
273+
$listener = new ResizeFormListener(TextType::class, [], false, false);
194274
$listener->onSubmit($event);
195275

196276
$this->assertEquals($data, $event->getData());
@@ -200,30 +280,30 @@ public function testOnSubmitNormDataRequiresArrayOrTraversable()
200280
{
201281
$this->expectException(UnexpectedTypeException::class);
202282
$data = 'no array or traversable';
203-
$event = new FormEvent($this->form, $data);
204-
$listener = new ResizeFormListener('text', [], false, false);
283+
$event = new FormEvent($this->builder->getForm(), $data);
284+
$listener = new ResizeFormListener(TextType::class, [], false, false);
205285
$listener->onSubmit($event);
206286
}
207287

208288
public function testOnSubmitNormDataDealsWithNullData()
209289
{
210-
$this->form->add($this->getForm('1'));
290+
$this->builder->add($this->getBuilder('1'));
211291

212292
$data = null;
213-
$event = new FormEvent($this->form, $data);
214-
$listener = new ResizeFormListener('text', [], false, true);
293+
$event = new FormEvent($this->builder->getForm(), $data);
294+
$listener = new ResizeFormListener(TextType::class, [], false, true);
215295
$listener->onSubmit($event);
216296

217297
$this->assertEquals([], $event->getData());
218298
}
219299

220300
public function testOnSubmitDealsWithObjectBackedIteratorAggregate()
221301
{
222-
$this->form->add($this->getForm('1'));
302+
$this->builder->add($this->getBuilder('1'));
223303

224304
$data = new \ArrayObject([0 => 'first', 1 => 'second', 2 => 'third']);
225-
$event = new FormEvent($this->form, $data);
226-
$listener = new ResizeFormListener('text', [], false, true);
305+
$event = new FormEvent($this->builder->getForm(), $data);
306+
$listener = new ResizeFormListener(TextType::class, [], false, true);
227307
$listener->onSubmit($event);
228308

229309
$this->assertArrayNotHasKey(0, $event->getData());
@@ -232,11 +312,11 @@ public function testOnSubmitDealsWithObjectBackedIteratorAggregate()
232312

233313
public function testOnSubmitDealsWithArrayBackedIteratorAggregate()
234314
{
235-
$this->form->add($this->getForm('1'));
315+
$this->builder->add($this->getBuilder('1'));
236316

237317
$data = new ArrayCollection([0 => 'first', 1 => 'second', 2 => 'third']);
238-
$event = new FormEvent($this->form, $data);
239-
$listener = new ResizeFormListener('text', [], false, true);
318+
$event = new FormEvent($this->builder->getForm(), $data);
319+
$listener = new ResizeFormListener(TextType::class, [], false, true);
240320
$listener->onSubmit($event);
241321

242322
$this->assertArrayNotHasKey(0, $event->getData());
@@ -245,46 +325,37 @@ public function testOnSubmitDealsWithArrayBackedIteratorAggregate()
245325

246326
public function testOnSubmitDeleteEmptyNotCompoundEntriesIfAllowDelete()
247327
{
248-
$this->form->setData(['0' => 'first', '1' => 'second']);
249-
$this->form->add($this->getForm('0'));
250-
$this->form->add($this->getForm('1'));
251-
252-
$data = [0 => 'first', 1 => ''];
253-
foreach ($data as $child => $dat) {
254-
$this->form->get($child)->submit($dat);
255-
}
256-
$event = new FormEvent($this->form, $data);
257-
$listener = new ResizeFormListener('text', [], false, true, true);
258-
$listener->onSubmit($event);
328+
$this->builder->setData(['0' => 'first', '1' => 'second']);
329+
$this->builder->add($this->getBuilder('0'));
330+
$this->builder->add($this->getBuilder('1'));
331+
$this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, [], false, true, true));
259332

260-
$this->assertEquals([0 => 'first'], $event->getData());
333+
$form = $this->builder->getForm();
334+
335+
$form->submit([0 => 'first', 1 => '']);
336+
337+
$this->assertEquals([0 => 'first'], $form->getData());
261338
}
262339

263340
public function testOnSubmitDeleteEmptyCompoundEntriesIfAllowDelete()
264341
{
265-
$this->form->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]);
266-
$form1 = $this->getBuilder('0')
267-
->setCompound(true)
268-
->setDataMapper(new DataMapper())
269-
->getForm();
270-
$form1->add($this->getForm('name'));
271-
$form2 = $this->getBuilder('1')
272-
->setCompound(true)
273-
->setDataMapper(new DataMapper())
274-
->getForm();
275-
$form2->add($this->getForm('name'));
276-
$this->form->add($form1);
277-
$this->form->add($form2);
278-
279-
$data = ['0' => ['name' => 'John'], '1' => ['name' => '']];
280-
foreach ($data as $child => $dat) {
281-
$this->form->get($child)->submit($dat);
282-
}
283-
$event = new FormEvent($this->form, $data);
284-
$callback = fn ($data) => null === $data['name'];
285-
$listener = new ResizeFormListener('text', [], false, true, $callback);
286-
$listener->onSubmit($event);
342+
$this->builder->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]);
343+
$this->builder->add('0', NestedType::class);
344+
$this->builder->add('1', NestedType::class);
345+
$callback = fn ($data) => empty($data['name']);
346+
$this->builder->addEventSubscriber(new ResizeFormListener(NestedType::class, [], false, true, $callback));
347+
348+
$form = $this->builder->getForm();
349+
$form->submit(['0' => ['name' => 'John'], '1' => ['name' => '']]);
287350

288-
$this->assertEquals(['0' => ['name' => 'John']], $event->getData());
351+
$this->assertEquals(['0' => ['name' => 'John']], $form->getData());
352+
}
353+
}
354+
355+
class NestedType extends AbstractType
356+
{
357+
public function buildForm(FormBuilderInterface $builder, array $options): void
358+
{
359+
$builder->add('name');
289360
}
290361
}

0 commit comments

Comments
 (0)
Please sign in to comment.