8000 [Form] Support event listeners in buttons · symfony/symfony@cf9ab1e · GitHub
[go: up one dir, main page]

Skip to content

Commit cf9ab1e

Browse files
committed
[Form] Support event listeners in buttons
1 parent 85d1011 commit cf9ab1e

File tree

10 files changed

+97
-27
lines changed

10 files changed

+97
-27
lines changed

UPGRADE-4.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Form
8787
reference date is deprecated.
8888
* Using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` is deprecated.
8989
* Overriding the methods `FormIntegrationTestCase::setUp()`, `TypeTestCase::setUp()` and `TypeTestCase::tearDown()` without the `void` return-type is deprecated.
90+
* The `ButtonBuilder::__construct()` method second argument should be an `EventDispatcherInterface` instance. The existing array `$options` argument has been moved to the third position.
9091

9192
FrameworkBundle
9293
---------------

src/Symfony/Component/Form/Button.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Form;
1313

14+
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
15+
use Symfony\Component\Form\Event\PostSetDataEvent;
16+
use Symfony\Component\Form\Event\PostSubmitEvent;
17+
use Symfony\Component\Form\Event\PreSubmitEvent;
1418
use Symfony\Component\Form\Exception\AlreadySubmittedException;
1519
use Symfony\Component\Form\Exception\BadMethodCallException;
1620

@@ -200,6 +204,11 @@ public function getErrors($deep = false, $flatten = true)
200204
*/
201205
public function setData($modelData)
202206
{
207+
$dispatcher = LegacyEventDispatcherProxy::decorate($this->config->getEventDispatcher());
208+
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
209+
$dispatcher->dispatch(new PostSetDataEvent($this, $modelData), FormEvents::POST_SET_DATA);
210+
}
211+
203212
// no-op, called during initialization of the form tree
204213
return $this;
205214
}
@@ -384,8 +393,17 @@ public function submit($submittedData, $clearMissing = true)
384393
throw new AlreadySubmittedException('A form can only be submitted once');
385394
}
386395

396+
$dispatcher = LegacyEventDispatcherProxy::decorate($this->config->getEventDispatcher());
397+
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
398+
$dispatcher->dispatch(new PreSubmitEvent($this, $submittedData), FormEvents::PRE_SUBMIT);
399+
}
400+
387401
$this->submitted = true;
388402

403+
if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) {
404+
$dispatcher->dispatch(new PostSubmitEvent($this, $submittedData), FormEvents::POST_SUBMIT);
405+
}
406+
389407
return $this;
390408
}
391409

src/Symfony/Component/Form/ButtonBuilder.php

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
namespace Symfony\Component\Form;
1313

14+
use Symfony\Component\EventDispatcher\EventDispatcher;
15+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1416
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
17+
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
1518
use Symfony\Component\Form\Exception\BadMethodCallException;
1619
use Symfony\Component\Form\Exception\InvalidArgumentException;
1720

@@ -22,6 +25,11 @@
2225
*/
2326
class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
2427
{
28+
private const UNSUPPORTED_FORM_EVENTS = [
29+
FormEvents::PRE_SET_DATA => [FormEvents::POST_SET_DATA],
30+
FormEvents::SUBMIT => [FormEvents::PRE_SUBMIT, FormEvents::POST_SUBMIT],
31+
];
32+
2533
protected $locked = false;
2634

2735
/**
@@ -49,16 +57,36 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
4957
*/
5058
private $options;
5159

60+
private $dispatcher;
61+
5262
/**
5363
* @throws InvalidArgumentException if the name is empty
5464
*/
55-
public function __construct(?string $name, array $options = [])
65+
public function __construct(?string $name, /*EventDispatcherInterface*/ $dispatcher = [], array $options = [])
5666
{
67+
if (\func_num_args() > 1) {
68+
if (!$dispatcher instanceof EventDispatcherInterface) {
69+
@trigger_error(sprintf('The "%s" method second argument must be an instance of "%s" since Symfony 4.4. The existing array "$options" argument has been moved to the third position.', __METHOD__, EventDispatcherInterface::class), E_USER_DEPRECATED);
70+
71+
if (!\is_array($dispatcher)) {
72+
throw new \TypeError(sprintf('The second argument passed to the "%s" method must be an instance of "%s" or an array, "%s" given.', __METHOD__, EventDispatcherInterface::class, \is_object($dispatcher) ? \get_class($dispatcher) : \gettype($dispatcher)));
73+
}
74+
75+
$options = $dispatcher;
76+
$dispatcher = new EventDispatcher();
77+
}
78+
} else {
79+
@trigger_error(sprintf('The "%s" method requires an instance of "%s" as its second argument since Symfony 4.4.', __METHOD__, EventDispatcherInterface::class), E_USER_DEPRECATED);
80+
81+
$dispatcher = new EventDispatcher();
82+
}
83+
5784
if ('' === $name || null === $name) {
5885
throw new InvalidArgumentException('Buttons cannot have empty names.');
5986
}
6087

6188
$this->name = $name;
89+
$this->dispatcher = $dispatcher;
6290
$this->options = $options;
6391

6492
if (preg_match('/^([^a-z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) {
@@ -159,31 +187,45 @@ public function getForm()
159187
}
160188

161189
/**
162-
* Unsupported method.
163-
*
164-
* This method should not be invoked.
165-
*
166-
* @param string $eventName
167-
* @param callable $listener
168-
* @param int $priority
190+
* {@inheritdoc}
169191
*
170192
* @throws BadMethodCallException
171193
*/
172194
public function addEventListener($eventName, $listener, $priority = 0)
173195
{
174-
throw new BadMethodCallException('Buttons do not support event listeners.');
196+
if ($this->locked) {
197+
throw new BadMethodCallException('ButtonBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
198+
}
199+
200+
if (isset(self::UNSUPPORTED_FORM_EVENTS[$eventName])) {
201+
throw new BadMethodCallException(sprintf('Buttons do not support the "%s" form event. Use "%s" instead.', $eventName, implode('" or "', self::UNSUPPORTED_FORM_EVENTS[$eventName])));
202+
}
203+
204+
$this->dispatcher->addListener($eventName, $listener, $priority);
205+
206+
return $this;
175207
}
176208

177209
/**
178-
* Unsupported method.
179-
*
180-
* This method should not be invoked.
210+
* {@inheritdoc}
181211
*
182212
* @throws BadMethodCallException
183213
*/
184214
public function addEventSubscriber(EventSubscriberInterface $subscriber)
185215
{
186-
throw new BadMethodCallException('Buttons do not support event subscribers.');
216+
if ($this->locked) {
217+
throw new BadMethodCallException('ButtonBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
218+
}
219+
220+
foreach ($subscriber::getSubscribedEvents() as $eventName => $_) {
221+
if (isset(self::UNSUPPORTED_FORM_EVENTS[$eventName])) {
222+
throw new BadMethodCallException(sprintf('Buttons do not support the "%s" form event. Use "%s" instead.', $eventName, implode('" or "', self::UNSUPPORTED_FORM_EVENTS[$eventName])));
223+
}
224+
}
225+
226+
$this->dispatcher->addSubscriber($subscriber);
227+
228+
return $this;
187229
}
188230

189231
/**
@@ -513,11 +555,15 @@ public function getFormConfig()
513555
}
514556

515557
/**
516-
* Unsupported method.
558+
* {@inheritdoc}
517559
*/
518560
public function getEventDispatcher()
519561
{
520-
return null;
562+
if ($this->locked && !$this->dispatcher instanceof ImmutableEventDispatcher) {
563+
$this->dispatcher = new ImmutableEventDispatcher($this->dispatcher);
564+
}
565+
566+
return $this->dispatcher;
521567
}
522568

523569
/**

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ CHANGELOG
1111
* The type guesser guesses the HTML accept attribute when a mime type is configured in the File or Image constraint.
1212
* Overriding the methods `FormIntegrationTestCase::setUp()`, `TypeTestCase::setUp()` and `TypeTestCase::tearDown()` without the `void` return-type is deprecated.
1313
* marked all dispatched event classes as `@final`
14+
* The `ButtonBuilder::__construct()` method second argument should be an `EventDispatcherInterface` instance. The existing array `$options` argument has been moved to the third position.
15+
* Added support for event listeners on buttons.
1416

1517
4.3.0
1618
-----

src/Symfony/Component/Form/ResolvedFormType.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ public function getOptionsResolver()
206206
protected function newBuilder($name, $dataClass, FormFactoryInterface $factory, array $options)
207207
{
208208
if ($this->innerType instanceof ButtonTypeInterface) {
209-
return new ButtonBuilder($name, $options);
209+
return new ButtonBuilder($name, new EventDispatcher(), $options);
210210
}
211211

212212
if ($this->innerType instanceof SubmitButtonTypeInterface) {
213-
return new SubmitButtonBuilder($name, $options);
213+
return new SubmitButtonBuilder($name, new EventDispatcher(), $options);
214214
}
215215

216216
return new FormBuilder($name, $dataClass, new EventDispatcher(), $factory, $options);

src/Symfony/Component/Form/Tests/ButtonBuilderTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\EventDispatcher\EventDispatcher;
1516
use Symfony\Component\Form\ButtonBuilder;
1617
use Symfony\Component\Form\Exception\InvalidArgumentException;
1718

@@ -36,15 +37,15 @@ public function getValidNames()
3637
*/
3738
public function testValidNames($name)
3839
{
39-
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder($name));
40+
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder($name, new EventDispatcher()));
4041
}
4142

4243
/**
4344
* @group legacy
4445
*/
4546
public function testNameContainingIllegalCharacters()
4647
{
47-
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder('button[]'));
48+
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder('button[]', new EventDispatcher()));
4849
}
4950

5051
public function getInvalidNames()
@@ -63,6 +64,6 @@ public function testInvalidNames($name)
6364
{
6465
$this->expectException(InvalidArgumentException::class);
6566
$this->expectExceptionMessage('Buttons cannot have empty names.');
66-
new ButtonBuilder($name);
67+
new ButtonBuilder($name, new EventDispatcher());
6768
}
6869
}

src/Symfony/Component/Form/Tests/ButtonTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\EventDispatcher\EventDispatcher;
1516
use Symfony\Component\Form\ButtonBuilder;
1617
use Symfony\Component\Form\FormBuilder;
1718

@@ -75,7 +76,7 @@ public function getDisabledStates()
7576

7677
private function getButtonBuilder($name)
7778
{
78-
return new ButtonBuilder($name);
79+
return new ButtonBuilder($name, new EventDispatcher());
7980
}
8081

8182
private function getFormBuilder()

src/Symfony/Component/Form/Tests/CompoundFormTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ public function testNoClickedButtonBeforeSubmission()
997997
public function testNoClickedButton()
998998
{
999999
$button = $this->getMockBuilder('Symfony\Component\Form\SubmitButton')
1000-
->setConstructorArgs([new SubmitButtonBuilder('submit')])
1000+
->setConstructorArgs([new SubmitButtonBuilder('submit', new EventDispatcher())])
10011001
->setMethods(['isClicked'])
10021002
->getMock();
10031003

@@ -1019,7 +1019,7 @@ public function testNoClickedButton()
10191019
public function testClickedButton()
10201020
{
10211021
$button = $this->getMockBuilder('Symfony\Component\Form\SubmitButton')
1022-
->setConstructorArgs([new SubmitButtonBuilder('submit')])
1022+
->setConstructorArgs([new SubmitButtonBuilder('submit', new EventDispatcher())])
10231023
->setMethods(['isClicked'])
10241024
->getMock();
10251025

@@ -1073,7 +1073,7 @@ public function testClickedButtonFromParentForm()
10731073

10741074
public function testDisabledButtonIsNotSubmitted()
10751075
{
1076-
$button = new SubmitButtonBuilder('submit');
1076+
$button = new SubmitButtonBuilder('submit', new EventDispatcher());
10771077
$submit = $button
10781078
->setDisabled(true)
10791079
->getForm();

src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ private function getCompoundForm($data, array $options = [])
803803

804804
private function getSubmitButton($name = 'name', array $options = [])
805805
{
806-
$builder = new SubmitButtonBuilder($name, $options);
806+
$builder = new SubmitButtonBuilder($name, new EventDispatcher(), $options);
807807

808808
return $builder->getForm();
809809
}

src/Symfony/Component/Form/Tests/FormBuilderTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\EventDispatcher\EventDispatcher;
1516
use Symfony\Component\Form\ButtonBuilder;
1617
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
1718
use Symfony\Component\Form\FormBuilder;
@@ -154,8 +155,8 @@ public function testCreateNoTypeNo()
154155

155156
public function testAddButton()
156157
{
157-
$this->builder->add(new ButtonBuilder('reset'));
158-
$this->builder->add(new SubmitButtonBuilder('submit'));
158+
$this->builder->add(new ButtonBuilder('reset', new EventDispatcher()));
159+
$this->builder->add(new SubmitButtonBuilder('submit', new EventDispatcher()));
159160

160161
$this->assertCount(2, $this->builder->all());
161162
}

0 commit comments

Comments
 (0)
0