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 f487c6c

Browse filesBrowse files
committed
[Form] Added a "choice_filter" option to ChoiceType
1 parent ff9b8da commit f487c6c
Copy full SHA for f487c6c

19 files changed

+713
-40
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
+50-9Lines changed: 50 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,61 @@ 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 = \func_num_args() > 2 ? func_get_arg(2) : null;
95+
8696
if ($choices instanceof \Traversable) {
8797
$choices = iterator_to_array($choices);
8898
}
8999

90-
// Only cache per value when needed. The value is not validated on purpose.
100+
$cache = true;
101+
// Only cache per value and filter when needed. The value is not validated on purpose.
91102
// The decorated factory may decide which values to accept and which not.
92103
if ($value instanceof Cache\ChoiceValue) {
93104
$value = $value->getOption();
94105
} elseif ($value) {
95-
return $this->decoratedFactory->createListFromChoices($choices, $value);
106+
$cache = false;
107+
}
108+
if ($filter instanceof Cache\ChoiceFilter) {
109+
$filter = $filter->getOption();
110+
} elseif ($filter) {
111+
$cache = false;
112+
}
113+
114+
if (!$cache) {
115+
return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
96116
}
97117

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

100120
if (!isset($this->lists[$hash])) {
101-
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value);
121+
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
102122
}
103123

104124
return $this->lists[$hash];
105125
}
106126

107127
/**
108128
* {@inheritdoc}
129+
*
130+
* @param ChoiceLoaderInterface|Cache\ChoiceLoader $loader The loader or static loader to load
131+
* the choices lazily
132+
* @param callable|Cache\ChoiceValue|null $value The callable or static option for
133+
* generating the choice values
134+
* @param callable|Cache\ChoiceFilter|null $filter The callable or static option for
135+
* filtering the choices
109136
*/
110-
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
137+
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
111138
{
139+
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;
140+
112141
$cache = true;
113142

114143
if ($loader instanceof Cache\ChoiceLoader) {
@@ -123,21 +152,33 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
123152
$cache = false;
124153
}
125154

155+
if ($filter instanceof Cache\ChoiceFilter) {
156+
$filter = $filter->getOption();
157+
} elseif ($filter) {
158+
$cache = false;
159+
}
160+
126161
if (!$cache) {
127-
return $this->decoratedFactory->createListFromLoader($loader, $value);
162+
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
128163
}
129164

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

132167
if (!isset($this->lists[$hash])) {
133-
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value);
168+
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
134169
}
135170

136171
return $this->lists[$hash];
137172
}
138173

139174
/**
140175
* {@inheritdoc}
176+
*
177+
* @param array|callable|Cache\PreferredChoice|null $preferredChoices The preferred choices
178+
* @param callable|false|Cache\ChoiceLabel|null $label The option or static option generating the choice labels
179+
* @param callable|Cache\ChoiceFieldName|null $index The option or static option generating the view indices
180+
* @param callable|Cache\GroupBy|null $groupBy The option or static option generating the group names
181+
* @param array|callable|Cache\ChoiceAttr|null $attr The option or static option generating the HTML attributes
141182
*/
142183
public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null)
143184
{

‎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
+26-2Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
1515
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
1616
use Symfony\Component\Form\ChoiceList\LazyChoiceList;
17+
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
1718
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
19+
use Symfony\Component\Form\ChoiceList\Loader\FilterChoiceLoaderDecorator;
1820
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
1921
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
2022
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
@@ -23,22 +25,44 @@
2325
* Default implementation of {@link ChoiceListFactoryInterface}.
2426
*
2527
* @author Bernhard Schussek <bschussek@gmail.com>
28+
* @author Jules Pietri <jules@heahprod.com>
2629
*/
2730
class DefaultChoiceListFactory implements ChoiceListFactoryInterface
2831
{
2932
/**
3033
* {@inheritdoc}
34+
*
35+
* @param callable|null $filter
3136
*/
32-
public function createListFromChoices(iterable $choices, callable $value = null)
37+
public function createListFromChoices(iterable $choices, callable $value = null/*, callable $filter = null*/)
3338
{
39+
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;
40+
41+
if ($filter) {
42+
// filter the choice list lazily
43+
return $this->createListFromLoader(new FilterChoiceLoaderDecorator(
44+
new CallbackChoiceLoader(function () use ($choices) {
45+
return $choices;
46+
}
47+
), $filter), $value);
48+
}
49+
3450
return new ArrayChoiceList($choices, $value);
3551
}
3652

3753
/**
3854
* {@inheritdoc}
55+
*
56+
* @param callable|null $filter
3957
*/
40-
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null)
58+
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/*, callable $filter = null*/)
4159
{
60+
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;
61+
62+
if ($filter) {
63+
$loader = new FilterChoiceLoaderDecorator($loader, $filter);
64+
}
65+
4266
return new LazyChoiceList($loader, $value);
4367
}
4468

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php
+42-8Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,17 @@ 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 = \func_num_args() > 2 ? func_get_arg(2) : null;
72+
6973
if (\is_string($value)) {
7074
$value = new PropertyPath($value);
7175
}
@@ -81,19 +85,36 @@ public function createListFromChoices(iterable $choices, $value = null)
8185
};
8286
}
8387

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

87104
/**
88105
* {@inheritdoc}
89106
*
90-
* @param callable|string|PropertyPath|null $value The callable or path for
91-
* generating the choice values
107+
* @param callable|string|PropertyPath|null $value The callable or path for
108+
* generating the choice values
109+
* @param callable|string|PropertyPath|null $filter The callable or path for
110+
* filtering the choices
92111
*
93112
* @return ChoiceListInterface The choice list
94113
*/
95-
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
114+
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
96115
{
116+
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;
117+
97118
if (\is_string($value)) {
98119
$value = new PropertyPath($value);
99120
}
@@ -109,7 +130,20 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
109130
};
110131
}
111132

112-
return $this->decoratedFactory->createListFromLoader($loader, $value);
133+
if (\is_string($filter)) {
134+
$filter = new PropertyPath($filter);
135+
}
136+
137+
if ($filter instanceof PropertyPath) {
138+
$accessor = $this->propertyAccessor;
139+
$filter = static function ($choice) use ($accessor, $filter) {
140+
if (\is_object($choice) || \is_array($choice)) {
141+
return (bool) $accessor->getValue($choice, $filter);
142+
}
143+
};
144+
}
145+
146+
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
113147
}
114148

115149
/**

0 commit comments

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