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 002af90

Browse filesBrowse files
committed
[Form] Added a "choice_filter" option to ChoiceType
1 parent 269c4a2 commit 002af90
Copy full SHA for 002af90

18 files changed

+606
-35
lines changed

‎UPGRADE-5.1.md

Copy file name to clipboardExpand all lines: UPGRADE-5.1.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Form
2323
is deprecated. The method will be added to the interface in 6.0.
2424
* Implementing the `FormConfigBuilderInterface` without implementing the `setIsEmptyCallback()` method
2525
is deprecated. The method will be added to the interface in 6.0.
26+
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices` and `createListFromLoader` - not defining them is deprecated.
2627

2728
FrameworkBundle
2829
---------------

‎UPGRADE-6.0.md

Copy file name to clipboardExpand all lines: UPGRADE-6.0.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Form
2121

2222
* Added the `getIsEmptyCallback()` method to the `FormConfigInterface`.
2323
* Added the `setIsEmptyCallback()` method to the `FormConfigBuilderInterface`.
24+
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices` and `createListFromLoader`.
2425

2526
FrameworkBundle
2627
---------------

‎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
@@ -4,6 +4,8 @@ CHANGELOG
44
5.1.0
55
-----
66

7+
* Added a `choice_filter` option to `ChoiceType`
8+
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices` and `createListFromLoader` - not defining them is deprecated.
79
* Added a `ChoiceList` facade to leverage explicit choice list caching based on options
810
* Added an `AbstractChoiceLoader` to simplify implementations and handle global optimizations
911
* The `view_timezone` option defaults to the `model_timezone` if no `reference_date` is configured.

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/ChoiceList.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
1515
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
16+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFilter;
1617
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
1718
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
1819
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
@@ -66,6 +67,16 @@ public static function value($formType, $value, $vary = null): ChoiceValue
6667
return new ChoiceValue($formType, $value, $vary);
6768
}
6869

70+
/**
71+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
72+
* @param callable $filter Any pseudo callable to filter a choice list
73+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
74+
*/
75+
public static function filter($formType, $filter, $vary = null): ChoiceFilter
76+
{
77+
return new ChoiceFilter($formType, $filter, $vary);
78+
}
79+
6980
/**
7081
* Decorates a "choice_label" option to make it cacheable.
7182
*
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
13+
14+
use Symfony\Component\Form\FormTypeExtensionInterface;
15+
use Symfony\Component\Form\FormTypeInterface;
16+
17+
/**
18+
* A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
19+
* which configures a "choice_filter" option.
20+
*
21+
* @internal
22+
*
23+
* @author Jules Pietri <jules@heahprod.com>
24+
*/
25+
final class ChoiceFilter extends AbstractStaticOption
26+
{
27+
}

‎src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php
+56-9Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
/**
2020
* Caches the choice lists created by the decorated factory.
2121
*
22+
* To cache a list based on its options, arguments must be decorated
23+
* by a {@see Cache\AbstractStaticOption} implementation.
24+
*
2225
* @author Bernhard Schussek <bschussek@gmail.com>
2326
* @author Jules Pietri <jules@heahprod.com>
2427
*/
@@ -80,35 +83,67 @@ public function getDecoratedFactory()
8083

8184
/**
8285
* {@inheritdoc}
86+
*
87+
* @param callable|Cache\ChoiceValue|null $value The callable or static option for
88+
* generating the choice values
89+
* @param callable|Cache\ChoiceFilter|null $filter The callable or static option for
90+
* filtering the choices
8391
*/
84-
public function createListFromChoices(iterable $choices, $value = null)
92+
public function createListFromChoices(iterable $choices, $value = null/*, $filter = null*/)
8593
{
94+
$filter = null;
95+
if (\func_num_args() > 2) {
96+
$filter = func_get_arg(2);
97+
}
98+
8699
if ($choices instanceof \Traversable) {
87100
$choices = iterator_to_array($choices);
88101
}
89102

90-
// Only cache per value when needed. The value is not validated on purpose.
103+
$cache = true;
104+
// Only cache per value and filter when needed. The value is not validated on purpose.
91105
// The decorated factory may decide which values to accept and which not.
92106
if ($value instanceof Cache\ChoiceValue) {
93107
$value = $value->getOption();
94108
} elseif ($value) {
95-
return $this->decoratedFactory->createListFromChoices($choices, $value);
109+
$cache = false;
110+
}
111+
if ($filter instanceof Cache\ChoiceFilter) {
112+
$filter = $filter->getOption();
113+
} elseif ($filter) {
114+
$cache = false;
115+
}
116+
117+
if (!$cache) {
118+
return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
96119
}
97120

98-
$hash = self::generateHash([$choices, $value], 'fromChoices');
121+
$hash = self::generateHash([$choices, $value, $filter], 'fromChoices');
99122

100123
if (!isset($this->lists[$hash])) {
101-
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value);
124+
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
102125
}
103126

104127
return $this->lists[$hash];
105128
}
106129

107130
/**
108131
* {@inheritdoc}
132+
*
133+
* @param ChoiceLoaderInterface|Cache\ChoiceLoader $loader The loader or static loader to load
134+
* the choices lazily
135+
* @param callable|Cache\ChoiceValue|null $value The callable or static option for
136+
* generating the choice values
137+
* @param callable|Cache\ChoiceFilter|null $filter The callable or static option for
138+
* filtering the choices
109139
*/
110-
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
140+
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
111141
{
142+
$filter = null;
143+
if (\func_num_args() > 2) {
144+
$filter = func_get_arg(2);
145+
}
146+
112147
$cache = true;
113148

114149
if ($loader instanceof Cache\ChoiceLoader) {
@@ -123,21 +158,33 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
123158
$cache = false;
124159
}
125160

161+
if ($filter instanceof Cache\ChoiceFilter) {
162+
$filter = $filter->getOption();
163+
} elseif ($filter) {
164+
$cache = false;
165+
}
166+
126167
if (!$cache) {
127-
return $this->decoratedFactory->createListFromLoader($loader, $value);
168+
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
128169
}
129170

130-
$hash = self::generateHash([$loader, $value], 'fromLoader');
171+
$hash = self::generateHash([$loader, $value, $filter], 'fromLoader');
131172

132173
if (!isset($this->lists[$hash])) {
133-
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value);
174+
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
134175
}
135176

136177
return $this->lists[$hash];
137178
}
138179

139180
/**
140181
* {@inheritdoc}
182+
*
183+
* @param array|callable|Cache\PreferredChoice|null $preferredChoices The preferred choices
184+
* @param callable|false|Cache\ChoiceLabel|null $label The option or static option generating the choice labels
185+
* @param callable|Cache\ChoiceFieldName|null $index The option or static option generating the view indices
186+
* @param callable|Cache\GroupBy|null $groupBy The option or static option generating the group names
187+
* @param array|callable|Cache\ChoiceAttr|null $attr The option or static option generating the HTML attributes
141188
*/
142189
public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null)
143190
{

‎src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ interface ChoiceListFactoryInterface
3131
* The callable receives the choice as only argument.
3232
* Null may be passed when the choice list contains the empty value.
3333
*
34+
* @param callable|null $filter The callable filtering the choices
35+
*
3436
* @return ChoiceListInterface The choice list
3537
*/
36-
public function createListFromChoices(iterable $choices, callable $value = null);
38+
public function createListFromChoices(iterable $choices, callable $value = null/*, callable $filter = null*/);
3739

3840
/**
3941
* Creates a choice list that is loaded with the given loader.
@@ -42,9 +44,11 @@ public function createListFromChoices(iterable $choices, callable $value = null)
4244
* The callable receives the choice as only argument.
4345
* Null may be passed when the choice list contains the empty value.
4446
*
47+
* @param callable|null $filter The callable filtering the choices
48+
*
4549
* @return ChoiceListInterface The choice list
4650
*/
47-
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null);
51+
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/*, callable $filter = null*/);
4852

4953
/**
5054
* Creates a view for the given choice list.

‎src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php
+45-2Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
1616
use Symfony\Component\Form\ChoiceList\LazyChoiceList;
1717
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
18+
use Symfony\Component\Form\ChoiceList\Loader\FilterChoiceLoaderDecorator;
1819
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
1920
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
2021
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
@@ -28,17 +29,59 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
2829
{
2930
/**
3031
* {@inheritdoc}
32+
*
33+
* @param callable|null $filter
3134
*/
32-
public function createListFromChoices(iterable $choices, callable $value = null)
35+
public function createListFromChoices(iterable $choices, callable $value = null/*, callable $filter = null*/)
3336
{
37+
$filter = null;
38+
if (\func_num_args() > 2) {
39+
$filter = func_get_arg(2);
40+
}
41+
42+
if ($filter) {
43+
if ($choices instanceof \Traversable) {
44+
$choices = iterator_to_array($choices);
45+
}
46+
47+
foreach ($choices as $group => $choiceOrGroup) {
48+
if (!\is_array($choiceOrGroup)) {
49+
$choices = array_filter($choices, $filter);
50+
51+
break;
52+
}
53+
54+
// choices are structured, filter by group
55+
if ($filtered = array_filter($choiceOrGroup, $filter)) {
56+
$choices[$group] = $filtered;
57+
58+
continue;
59+
}
60+
61+
// also filter empty groups
62+
unset($choices[$group]);
63+
}
64+
}
65+
3466
return new ArrayChoiceList($choices, $value);
3567
}
3668

3769
/**
3870
* {@inheritdoc}
71+
*
72+
* @param callable|null $filter
3973
*/
40-
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null)
74+
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/*, callable $filter = null*/)
4175
{
76+
$filter = null;
77+
if (\func_num_args() > 2) {
78+
$filter = func_get_arg(2);
79+
}
80+
81+
if ($filter) {
82+
$loader = new FilterChoiceLoaderDecorator($loader, $filter);
83+
}
84+
4285
return new LazyChoiceList($loader, $value);
4386
}
4487

‎src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php
+48-8Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,20 @@ public function getDecoratedFactory()
5959
/**
6060
* {@inheritdoc}
6161
*
62-
* @param callable|string|PropertyPath|null $value The callable or path for
63-
* generating the choice values
62+
* @param callable|string|PropertyPath|null $value The callable or path for
63+
* generating the choice values
64+
* @param callable|string|PropertyPath|null $filter The callable or path for
65+
* filtering the choices
6466
*
6567
* @return ChoiceListInterface The choice list
6668
*/
67-
public function createListFromChoices(iterable $choices, $value = null)
69+
public function createListFromChoices(iterable $choices, $value = null/*, $filter = null*/)
6870
{
71+
$filter = null;
72+
if (\func_num_args() > 2) {
73+
$filter = func_get_arg(2);
74+
}
75+
6976
if (\is_string($value)) {
7077
$value = new PropertyPath($value);
7178
}
@@ -81,19 +88,39 @@ public function createListFromChoices(iterable $choices, $value = null)
8188
};
8289
}
8390

84-
return $this->decoratedFactory->createListFromChoices($choices, $value);
91+
if (\is_string($filter)) {
92+
$filter = new PropertyPath($filter);
93+
}
94+
95+
if ($filter instanceof PropertyPath) {
96+
$accessor = $this->propertyAccessor;
97+
$filter = static function ($choice) use ($accessor, $filter) {
98+
if (\is_object($choice) || \is_array($choice)) {
99+
return (bool) $accessor->getValue($choice, $filter);
100+
}
101+
};
102+
}
103+
104+
return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
85105
}
86106

87107
/**
88108
* {@inheritdoc}
89109
*
90-
* @param callable|string|PropertyPath|null $value The callable or path for
91-
* generating the choice values
110+
* @param callable|string|PropertyPath|null $value The callable or path for
111+
* generating the choice values
112+
* @param callable|string|PropertyPath|null $filter The callable or path for
113+
* filtering the choices
92114
*
93115
* @return ChoiceListInterface The choice list
94116
*/
95-
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
117+
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
96118
{
119+
$filter = null;
120+
if (\func_num_args() > 2) {
121+
$filter = func_get_arg(2);
122+
}
123+
97124
if (\is_string($value)) {
98125
$value = new PropertyPath($value);
99126
}
@@ -109,7 +136,20 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
109136
};
110137
}
111138

112-
return $this->decoratedFactory->createListFromLoader($loader, $value);
139+
if (\is_string($filter)) {
140+
$filter = new PropertyPath($filter);
141+
}
142+
143+
if ($filter instanceof PropertyPath) {
144+
$accessor = $this->propertyAccessor;
145+
$filter = static function ($choice) use ($accessor, $filter) {
146+
if (\is_object($choice) || \is_array($choice)) {
147+
return (bool) $accessor->getValue($choice, $filter);
148+
}
149+
};
150+
}
151+
152+
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
113153
}
114154

115155
/**

0 commit comments

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