Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5c69323

Browse filesBrowse files
committed
[Form] Is empty callback
1 parent f6e93de commit 5c69323
Copy full SHA for 5c69323

File tree

8 files changed

+125
-5
lines changed
Filter options

8 files changed

+125
-5
lines changed

‎src/Symfony/Component/Form/ButtonBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ButtonBuilder.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,16 @@ public function getFormConfig()
522522
return $config;
523523
}
524524

525+
/**
526+
* Unsupported method.
527+
*
528+
* @throws BadMethodCallException
529+
*/
530+
public function setIsEmptyCallback($isEmptyCallback)
531+
{
532+
throw new BadMethodCallException('Buttons do not support "is empty" callback.');
533+
}
534+
525535
/**
526536
* Unsupported method.
527537
*/
@@ -800,6 +810,16 @@ public function getOption($name, $default = null)
800810
return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
801811
}
802812

813+
/**
814+
* Unsupported method.
815+
*
816+
* @throws BadMethodCallException
817+
*/
818+
public function getIsEmptyCallback()
819+
{
820+
throw new BadMethodCallException('Buttons do not support "is empty" callback.');
821+
}
822+
803823
/**
804824
* Unsupported method.
805825
*

‎src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
3333
// doing so also calls setDataLocked(true).
3434
$builder->setData(isset($options['data']) ? $options['data'] : false);
3535
$builder->addViewTransformer(new BooleanToStringTransformer($options['value'], $options['false_values']));
36+
$builder->setIsEmptyCallback([__CLASS__, 'isEmpty']);
3637
}
3738

3839
/**
@@ -72,4 +73,12 @@ public function getBlockPrefix()
7273
{
7374
return 'checkbox';
7475
}
76+
77+
/**
78+
* @internal
79+
*/
80+
public static function isEmpty($modelData): bool
81+
{
82+
return false === $modelData;
83+
}
7584
}

‎src/Symfony/Component/Form/Form.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Form.php
+1-5Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -727,11 +727,7 @@ public function isEmpty()
727727
}
728728
}
729729

730-
return FormUtil::isEmpty($this->modelData) ||
731-
// arrays, countables
732-
((\is_array($this->modelData) || $this->modelData instanceof \Countable) && 0 === \count($this->modelData)) ||
733-
// traversables that are not countable
734-
($this->modelData instanceof \Traversable && 0 === iterator_count($this->modelData));
730+
return (bool) ($this->config->getIsEmptyCallback())($this->modelData);
735731
}
736732

737733
/**

‎src/Symfony/Component/Form/FormConfigBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/FormConfigBuilder.php
+35Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Form\Exception\BadMethodCallException;
1818
use Symfony\Component\Form\Exception\InvalidArgumentException;
1919
use Symfony\Component\Form\Exception\UnexpectedTypeException;
20+
use Symfony\Component\Form\Util\FormUtil;
2021
use Symfony\Component\PropertyAccess\PropertyPath;
2122
use Symfony\Component\PropertyAccess\PropertyPathInterface;
2223

@@ -104,6 +105,8 @@ class FormConfigBuilder implements FormConfigBuilderInterface
104105
private $autoInitialize = false;
105106
private $options;
106107

108+
private $isEmptyCallback;
109+
107110
/**
108111
* Creates an empty form configuration.
109112
*
@@ -127,6 +130,8 @@ public function __construct(?string $name, ?string $dataClass, EventDispatcherIn
127130
$this->dataClass = $dataClass;
128131
$this->dispatcher = $dispatcher;
129132
$this->options = $options;
133+
134+
$this->isEmptyCallback = [__CLASS__, 'isEmpty'];
130135
}
131136

132137
/**
@@ -464,6 +469,14 @@ public function getOption($name, $default = null)
464469
return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
465470
}
466471

472+
/**
473+
* @return callable
474+
*/
475+
public function getIsEmptyCallback()
476+
{
477+
return $this->isEmptyCallback;
478+
}
479+
467480
/**
468481
* {@inheritdoc}
469482
*/
@@ -764,6 +777,16 @@ public function getFormConfig()
764777
return $config;
765778
}
766779

780+
/**
781+
* @param callable(mixed $modelData) $isEmptyCallback
782+
*/
783+
public function setIsEmptyCallback($isEmptyCallback)
784+
{
785+
$this->isEmptyCallback = $isEmptyCallback;
786+
787+
return $this;
788+
}
789+
767790
/**
768791
* Validates whether the given variable is a valid form name.
769792
*
@@ -801,4 +824,16 @@ public static function isValidName(?string $name): bool
801824
{
802825
return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name);
803826
}
827+
828+
/**
829+
* @internal
830+
*/
831+
public static function isEmpty($modelData): bool
832+
{
833+
return FormUtil::isEmpty($modelData) ||
834+
// arrays, countables
835+
((\is_array($modelData) || $modelData instanceof \Countable) && 0 === \count($modelData)) ||
836+
// traversables that are not countable
837+
($modelData instanceof \Traversable && 0 === iterator_count($modelData));
838+
}
804839
}

‎src/Symfony/Component/Form/FormConfigBuilderInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/FormConfigBuilderInterface.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
/**
1818
* @author Bernhard Schussek <bschussek@gmail.com>
19+
*
20+
* @method $this setIsEmptyCallback($isEmptyCallback) Sets the callback that will be called to determine if the model data of the form is empty or not - not implementing it is deprecated since Symfony 4.4
1921
*/
2022
interface FormConfigBuilderInterface extends FormConfigInterface
2123
{

‎src/Symfony/Component/Form/FormConfigInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/FormConfigInterface.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* The configuration of a {@link Form} object.
1919
*
2020
* @author Bernhard Schussek <bschussek@gmail.com>
21+
*
22+
* @method getIsEmptyCallback() Returns a callable that takes the model data as argument and that returns if it is empty or not - not implementing it is deprecated since Symfony 4.4
2123
*/
2224
interface FormConfigInterface
2325
{

‎src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
+41Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2053,4 +2053,45 @@ public function provideTrimCases()
20532053
'Multiple expanded' => [true, true],
20542054
];
20552055
}
2056+
2057+
/**
2058+
* @dataProvider expandedIsEmptyWhenNoRealChoiceIsSelectedProvider
2059+
*/
2060+
public function testExpandedIsEmptyWhenNoRealChoiceIsSelected(bool $expected, $submittedData, bool $multiple, bool $required, $placeholder)
2061+
{
2062+
$options = [
2063+
'expanded' => true,
2064+
'choices' => [
2065+
'foo' => 'bar',
2066+
],
2067+
'multiple' => $multiple,
2068+
'required' => $required,
2069+
];
2070+
2071+
if (!$multiple) {
2072+
$options['placeholder'] = $placeholder;
2073+
}
2074+
2075+
$form = $this->factory->create(static::TESTED_TYPE, null, $options);
2076+
2077+
$form->submit($submittedData);
2078+
2079+
$this->assertSame($expected, $form->isEmpty());
2080+
}
2081+
2082+
public function expandedIsEmptyWhenNoRealChoiceIsSelectedProvider()
2083+
{
2084+
// Some invalid cases are voluntarily not tested :
2085+
// - multiple with placeholder
2086+
// - required with placeholder
2087+
return [
2088+
'Nothing submitted / single / not required / without a placeholder -> should be empty' => [true, null, false, false, null],
2089+
'Nothing submitted / single / not required / with a placeholder -> should not be empty' => [false, null, false, false, 'ccc'], // It falls back on the placeholder
2090+
'Nothing submitted / single / required / without a placeholder -> should be empty' => [true, null, false, true, null],
2091+
'Nothing submitted / single / required / with a placeholder -> should be empty' => [true, null, false, true, 'ccc'],
2092+
'Nothing submitted / multiple / not required / without a placeholder -> should be empty' => [true, null, true, false, null],
2093+
'Nothing submitted / multiple / required / without a placeholder -> should be empty' => [true, null, true, true, null],
2094+
'Placeholder submitted / single / not required / with a placeholder -> should not be empty' => [false, '', false, false, 'ccc'], // The placeholder is a selected value
2095+
];
2096+
}
20562097
}

‎src/Symfony/Component/Form/Tests/SimpleFormTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/SimpleFormTest.php
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,21 @@ public function testCannotCallGetViewDataInPreSetDataListener()
11261126
$form->setData('foo');
11271127
}
11281128

1129+
public function testIsEmptyCallback()
1130+
{
1131+
$config = new FormConfigBuilder('foo', null, $this->dispatcher);
1132+
1133+
$config->setIsEmptyCallback(function ($modelData): bool { return 'ccc' === $modelData; });
1134+
$form = new Form($config);
1135+
$form->setData('ccc');
1136+
$this->assertTrue($form->isEmpty());
1137+
1138+
$config->setIsEmptyCallback(function (): bool { return false; });
1139+
$form = new Form($config);
1140+
$form->setData(null);
1141+
$this->assertFalse($form->isEmpty());
1142+
}
1143+
11291144
protected function createForm()
11301145
{
11311146
return $this->getBuilder()->getForm();

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.