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

Browse filesBrowse files
committed
[Form] Is empty callback
1 parent f830226 commit 98df4f2
Copy full SHA for 98df4f2

11 files changed

+137
-5
lines changed

‎UPGRADE-4.4.md

Copy file name to clipboardExpand all lines: UPGRADE-4.4.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ Form
7575
* Using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a
7676
reference date is deprecated.
7777
* Using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` is deprecated.
78+
* Added `getIsEmptyCallback()` method to `FormConfigInterface`.
79+
* Added `setIsEmptyCallback()` method to `FormConfigBuilderInterface`.
7880

7981
FrameworkBundle
8082
---------------

‎UPGRADE-5.0.md

Copy file name to clipboardExpand all lines: UPGRADE-5.0.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ Form
211211
```
212212

213213
* The `regions` option was removed from the `TimezoneType`.
214+
* Added the `getIsEmptyCallback()` method to `FormConfigInterface`.
215+
* Added the `setIsEmptyCallback()` method to `FormConfigBuilderInterface`.
214216

215217
FrameworkBundle
216218
---------------

‎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
@@ -521,6 +521,16 @@ public function getFormConfig()
521521
return $config;
522522
}
523523

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

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

‎src/Symfony/Component/Form/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ CHANGELOG
99
* preferred choices are repeated in the list of all choices
1010
* deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`
1111
* The type guesser guesses the HTML accept attribute when a mime type is configured in the File or Image constraint.
12+
* added the `getIsEmptyCallback()` method to `FormConfigInterface`
13+
* added the `setIsEmptyCallback()` method to `FormConfigBuilderInterface`
1214

1315
4.3.0
1416
-----

‎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
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Form\AbstractType;
1515
use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer;
1616
use Symfony\Component\Form\FormBuilderInterface;
17+
use Symfony\Component\Form\FormConfigBuilderInterface;
1718
use Symfony\Component\Form\FormInterface;
1819
use Symfony\Component\Form\FormView;
1920
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -33,6 +34,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
3334
// doing so also calls setDataLocked(true).
3435
$builder->setData(isset($options['data']) ? $options['data'] : false);
3536
$builder->addViewTransformer(new BooleanToStringTransformer($options['value'], $options['false_values']));
37+
38+
if (!method_exists($builder, 'setIsEmptyCallback')) {
39+
@trigger_error(sprintf('Not implementing the "%s::setIsEmptyCallback()" method in "%s" is deprecated since Symfony 4.4.', FormConfigBuilderInterface::class, \get_class($builder)), E_USER_DEPRECATED);
40+
41+
return;
42+
}
43+
44+
$builder->setIsEmptyCallback(static function ($modelData): bool {
45+
return false === $modelData;
46+
});
3647
}
3748

3849
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Form.php
+7-5Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -727,11 +727,13 @@ 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+
if (!method_exists($this->config, 'getIsEmptyCallback')) {
731+
@trigger_error(sprintf('Not implementing the "%s::getIsEmptyCallback()" method in "%s" is deprecated since Symfony 4.4.', FormConfigInterface::class, \get_class($this->config)), E_USER_DEPRECATED);
732+
733+
return FormConfigBuilder::isEmpty($this->modelData);
734+
}
735+
736+
return $this->config->getIsEmptyCallback()($this->modelData);
735737
}
736738

737739
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/FormConfigBuilder.php
+33Lines changed: 33 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 = [__CLASS__, 'isEmpty'];
109+
107110
/**
108111
* Creates an empty form configuration.
109112
*
@@ -462,6 +465,14 @@ public function getOption($name, $default = null)
462465
return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
463466
}
464467

468+
/**
469+
* {@inheritdoc}
470+
*/
471+
public function getIsEmptyCallback(): callable
472+
{
473+
return $this->isEmptyCallback;
474+
}
475+
465476
/**
466477
* {@inheritdoc}
467478
*/
@@ -762,6 +773,16 @@ public function getFormConfig()
762773
return $config;
763774
}
764775

776+
/**
777+
* {@inheritdoc}
778+
*/
779+
public function setIsEmptyCallback(callable $isEmptyCallback)
780+
{
781+
$this->isEmptyCallback = $isEmptyCallback;
782+
783+
return $this;
784+
}
785+
765786
/**
766787
* Validates whether the given variable is a valid form name.
767788
*
@@ -799,4 +820,16 @@ public static function isValidName(?string $name): bool
799820
{
800821
return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name);
801822
}
823+
824+
/**
825+
* @internal
826+
*/
827+
public static function isEmpty($modelData): bool
828+
{
829+
return FormUtil::isEmpty($modelData) ||
830+
// arrays, countables
831+
((\is_array($modelData) || $modelData instanceof \Countable) && 0 === \count($modelData)) ||
832+
// traversables that are not countable
833+
($modelData instanceof \Traversable && 0 === iterator_count($modelData));
834+
}
802835
}

‎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(callable $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 callable 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
@@ -2056,4 +2056,45 @@ public function provideTrimCases()
20562056
'Multiple expanded' => [true, true],
20572057
];
20582058
}
2059+
2060+
/**
2061+
* @dataProvider expandedIsEmptyWhenNoRealChoiceIsSelectedProvider
2062+
*/
2063+
public function testExpandedIsEmptyWhenNoRealChoiceIsSelected(bool $expected, $submittedData, bool $multiple, bool $required, $placeholder)
2064+
{
2065+
$options = [
2066+
'expanded' => true,
2067+
'choices' => [
2068+
'foo' => 'bar',
2069+
],
2070+
'multiple' => $multiple,
2071+
'required' => $required,
2072+
];
2073+
2074+
if (!$multiple) {
2075+
$options['placeholder'] = $placeholder;
2076+
}
2077+
2078+
$form = $this->factory->create(static::TESTED_TYPE, null, $options);
2079+
2080+
$form->submit($submittedData);
2081+
2082+
$this->assertSame($expected, $form->isEmpty());
2083+
}
2084+
2085+
public function expandedIsEmptyWhenNoRealChoiceIsSelectedProvider()
2086+
{
2087+
// Some invalid cases are voluntarily not tested:
2088+
// - multiple with placeholder
2089+
// - required with placeholder
2090+
return [
2091+
'Nothing submitted / single / not required / without a placeholder -> should be empty' => [true, null, false, false, null],
2092+
'Nothing submitted / single / not required / with a placeholder -> should not be empty' => [false, null, false, false, 'ccc'], // It falls back on the placeholder
2093+
'Nothing submitted / single / required / without a placeholder -> should be empty' => [true, null, false, true, null],
2094+
'Nothing submitted / single / required / with a placeholder -> should be empty' => [true, null, false, true, 'ccc'],
2095+
'Nothing submitted / multiple / not required / without a placeholder -> should be empty' => [true, null, true, false, null],
2096+
'Nothing submitted / multiple / required / without a placeholder -> should be empty' => [true, null, true, true, null],
2097+
'Placeholder submitted / single / not required / with a placeholder -> should not be empty' => [false, '', false, false, 'ccc'], // The placeholder is a selected value
2098+
];
2099+
}
20592100
}

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