Skip to content

Commit f5b0ebe

Browse files
committed
[Form] Added support for caching choice lists based on options
1 parent b6794e2 commit f5b0ebe

19 files changed

+781
-56
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.1.0
55
-----
66

7+
* Added a `ChoiceList` facade to leverage explicit choice list caching based on options
78
* Added an `AbstractChoiceLoader` to simplify implementations and handle global optimizations
89
* The `view_timezone` option defaults to the `model_timezone` if no `reference_date` is configured.
910
* Added default `inputmode` attribute to Search, Email and Tel form types.

ChoiceList/ChoiceList.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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;
13+
14+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
15+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
16+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
17+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
18+
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
19+
use Symfony\Component\Form\ChoiceList\Factory\Cache\GroupBy;
20+
use Symfony\Component\Form\ChoiceList\Factory\Cache\PreferredChoice;
21+
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
22+
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
23+
use Symfony\Component\Form\FormTypeExtensionInterface;
24+
use Symfony\Component\Form\FormTypeInterface;
25+
26+
/**
27+
* A set of convenient static methods to create cacheable choice list options.
28+
*
29+
* @author Jules Pietri <jules@heahprod.com>
30+
*/
31+
final class ChoiceList
32+
{
33+
/**
34+
* Creates a cacheable loader from any callable providing iterable choices.
35+
*
36+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
37+
* @param callable $choices A callable that must return iterable choices or grouped choices
38+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the loader
39+
*/
40+
public static function lazy($formType, callable $choices, $vary = null): ChoiceLoader
41+
{
42+
return self::loader($formType, new CallbackChoiceLoader($choices), $vary);
43+
}
44+
45+
/**
46+
* Decorates a loader to make it cacheable.
47+
*
48+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
49+
* @param ChoiceLoaderInterface $loader A loader responsible for creating loading choices or grouped choices
50+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the loader
51+
*/
52+
public static function loader($formType, ChoiceLoaderInterface $loader, $vary = null): ChoiceLoader
53+
{
54+
return new ChoiceLoader($formType, $loader, $vary);
55+
}
56+
57+
/**
58+
* Decorates a "choice_value" callback to make it cacheable.
59+
*
60+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
61+
* @param callable $value Any pseudo callable to create a unique string value from a choice
62+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
63+
*/
64+
public static function value($formType, $value, $vary = null): ChoiceValue
65+
{
66+
return new ChoiceValue($formType, $value, $vary);
67+
}
68+
69+
/**
70+
* Decorates a "choice_label" option to make it cacheable.
71+
*
72+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
73+
* @param callable|false $label Any pseudo callable to create a label from a choice or false to discard it
74+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
75+
*/
76+
public static function label($formType, $label, $vary = null): ChoiceLabel
77+
{
78+
return new ChoiceLabel($formType, $label, $vary);
79+
}
80+
81+
/**
82+
* Decorates a "choice_name" callback to make it cacheable.
83+
*
84+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
85+
* @param callable $fieldName Any pseudo callable to create a field name from a choice
86+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
87+
*/
88+
public static function fieldName($formType, $fieldName, $vary = null): ChoiceFieldName
89+
{
90+
return new ChoiceFieldName($formType, $fieldName, $vary);
91+
}
92+
93+
/**
94+
* Decorates a "choice_attr" option to make it cacheable.
95+
*
96+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
97+
* @param callable|array $attr Any pseudo callable or array to create html attributes from a choice
98+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
99+
*/
100+
public static function attr($formType, $attr, $vary = null): ChoiceAttr
101+
{
102+
return new ChoiceAttr($formType, $attr, $vary);
103+
}
104+
105+
/**
106+
* Decorates a "group_by" callback to make it cacheable.
107+
*
108+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
109+
* @param callable $groupBy Any pseudo callable to return a group name from a choice
110+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
111+
*/
112+
public static function groupBy($formType, $groupBy, $vary = null): GroupBy
113+
{
114+
return new GroupBy($formType, $groupBy, $vary);
115+
}
116+
117+
/**
118+
* Decorates a "preferred_choices" option to make it cacheable.
119+
*
120+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
121+
* @param callable|array $preferred Any pseudo callable or array to return a group name from a choice
122+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
123+
*/
124+
public static function preferred($formType, $preferred, $vary = null): PreferredChoice
125+
{
126+
return new PreferredChoice($formType, $preferred, $vary);
127+
}
128+
129+
/**
130+
* Should not be instantiated.
131+
*/
132+
private function __construct()
133+
{
134+
}
135+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\ChoiceList\Factory\CachingFactoryDecorator;
15+
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
16+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
17+
use Symfony\Component\Form\FormTypeExtensionInterface;
18+
use Symfony\Component\Form\FormTypeInterface;
19+
20+
/**
21+
* A template decorator for static {@see ChoiceType} options.
22+
*
23+
* Used as fly weight for {@see CachingFactoryDecorator}.
24+
*
25+
* @internal
26+
*
27+
* @author Jules Pietri <jules@heahprod.com>
28+
*/
29+
abstract class AbstractStaticOption
30+
{
31+
private static $options = [];
32+
33+
/** @var bool|callable|string|array|\Closure|ChoiceLoaderInterface */
34+
private $option;
35+
36+
/**
37+
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
38+
* @param mixed $option Any pseudo callable, array, string or bool to define a choice list option
39+
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
40+
*/
41+
final public function __construct($formType, $option, $vary = null)
42+
{
43+
if (!$formType instanceof FormTypeInterface && !$formType instanceof FormTypeExtensionInterface) {
44+
throw new \TypeError(sprintf('Expected an instance of "%s" or "%s", but got "%s".', FormTypeInterface::class, FormTypeExtensionInterface::class, \is_object($formType) ? \get_class($formType) : \gettype($formType)));
45+
}
46+
47+
$hash = CachingFactoryDecorator::generateHash([static::class, $formType, $vary]);
48+
49+
$this->option = self::$options[$hash] ?? self::$options[$hash] = $option;
50+
}
51+
52+
/**
53+
* @return mixed
54+
*/
55+
final public function getOption()
56+
{
57+
return $this->option;
58+
}
59+
60+
final public static function reset(): void
61+
{
62+
self::$options = [];
63+
}
64+
}
Lines 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_attr" option.
20+
*
21+
* @internal
22+
*
23+
* @author Jules Pietri <jules@heahprod.com>
24+
*/
25+
final class ChoiceAttr extends AbstractStaticOption
26+
{
27+
}
Lines 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_name" callback.
20+
*
21+
* @internal
22+
*
23+
* @author Jules Pietri <jules@heahprod.com>
24+
*/
25+
final class ChoiceFieldName extends AbstractStaticOption
26+
{
27+
}
Lines 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_label" option.
20+
*
21+
* @internal
22+
*
23+
* @author Jules Pietri <jules@heahprod.com>
24+
*/
25+
final class ChoiceLabel extends AbstractStaticOption
26+
{
27+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\ChoiceList\Loader\ChoiceLoaderInterface;
15+
use Symfony\Component\Form\FormTypeExtensionInterface;
16+
use Symfony\Component\Form\FormTypeInterface;
17+
18+
/**
19+
* A cacheable wrapper for {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
20+
* which configures a "choice_loader" option.
21+
*
22+
* @internal
23+
*
24+
* @author Jules Pietri <jules@heahprod.com>
25+
*/
26+
final class ChoiceLoader extends AbstractStaticOption implements ChoiceLoaderInterface
27+
{
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function loadChoiceList(callable $value = null)
32+
{
33+
return $this->getOption()->loadChoiceList($value);
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function loadChoicesForValues(array $values, callable $value = null)
40+
{
41+
return $this->getOption()->loadChoicesForValues($values, $value);
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function loadValuesForChoices(array $choices, callable $value = null)
48+
{
49+
$this->getOption()->loadValuesForChoices($choices, $value);
50+
}
51+
}
Lines 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_value" callback.
20+
*
21+
* @internal
22+
*
23+
* @author Jules Pietri <jules@heahprod.com>
24+
*/
25+
final class ChoiceValue extends AbstractStaticOption
26+
{
27+
}

0 commit comments

Comments
 (0)