8000 feature #34550 [Form] Added an AbstractChoiceLoader to simplify imple… · symfony/form@b6794e2 · GitHub
[go: up one dir, main page]

Skip to content

Commit b6794e2

Browse files
committed
feature #34550 [Form] Added an AbstractChoiceLoader to simplify implementations and handle global optimizations (HeahDude)
This PR was merged into the 5.1-dev branch. Discussion ---------- [Form] Added an AbstractChoiceLoader to simplify implementations and handle global optimizations | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | ~ | License | MIT | Doc PR | ~ <!-- Replace this notice by a short README for your feature/bugfix. This will help people understand your PR and can be used as a start for the documentation. Additionally (see https://symfony.com/roadmap): - Always add tests and ensure they pass. - Never break backward compatibility (see https://symfony.com/bc). - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too.) - Features and deprecations must be submitted against branch master. --> Taking over #33218 (taking over #30983) The `ChoiceLoaderInterface` is not easy to understand/implement, while its goal is simple, lazy load an array of choices. What may seem complicated is the how/what to optimize loading, we need to deal with a `$value` callback (which refers to the `choice_value` option allowing to transform a model choice to a unique string value that can be displayed/submitted). We have now enough implementations in core to justify the need of an abstraction and provide a better DX in the process. Before this PR we needed to implement 3 methods to create a loader: - `loadChoiceList(?callable $value): ChoiceListInterface` - `loadChoicesForValues(array $values, ?callable $value): array` - `loadValuesForChoices(array $choices, ?callable $value): array` and handle optimization, in each. Now we only need to implement: - `loadChoices(): iterable` and optionnally: - `doLoadChoicesForValues(array $values, ?callable $value): array` if more optimization is needed to only load submitted values, (i.e the core intl loader prevents loading values that are the same as choices, and the doctrine one performs a `WHERE id IN ($ids)` query). Commits ------- 1394df2dea [Form] Added an AbstractChoiceLoader to simplify implementations and handle global optimizations
2 parents 46c5386 + 58c9047 commit b6794e2

File tree

4 files changed

+94
-59
lines changed

4 files changed

+94
-59
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 an `AbstractChoiceLoader` to simplify implementations and handle global optimizations
78
* The `view_timezone` option defaults to the `model_timezone` if no `reference_date` is configured.
89
* Added default `inputmode` attribute to Search, Email and Tel form types.
910
* Implementing the `FormConfigInterface` without implementing the `getIsEmptyCallback()` method
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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\Loader;
13+
14+
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
15+
16+
/**
17+
* @author Jules Pietri <jules@heahprod.com>
18+
*/
19+
abstract class AbstractChoiceLoader implements ChoiceLoaderInterface
20+
{
21+
/**
22+
* The loaded choice list.
23+
*
24+
* @var ArrayChoiceList
25+
*/
26+
private $choiceList;
27+
28+
/**
29+
* @final
30+
*
31+
* {@inheritdoc}
32+
*/
33+
public function loadChoiceList(callable $value = null)
34+
{
35+
return $this->choiceList ?? ($this->choiceList = new ArrayChoiceList($this->loadChoices(), $value));
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public function loadChoicesForValues(array $values, callable $value = null)
42+
{
43+
if (!$values) {
44+
return [];
45+
}
46+
47+
if ($this->choiceList) {
48+
return $this->choiceList->getChoicesForValues($values);
49+
}
50+
51+
return $this->doLoadChoicesForValues($values, $value);
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function loadValuesForChoices(array $choices, callable $value = null)
58+
{
59+
if (!$choices) {
60+
return [];
61+
}
62+
63+
if ($value) {
64+
// if a value callback exists, use it
65+
return array_map($value, $choices);
66+
}
67+
68+
if ($this->choiceList) {
69+
return $this->choiceList->getValuesForChoices($choices);
70+
}
71+
72+
return $this->doLoadValuesForChoices($choices);
73+
}
74+
75+
abstract protected function loadChoices(): iterable;
76+
77+
protected function doLoadChoicesForValues(array $values, ?callable $value): array
78+
{
79+
return $this->loadChoiceList($value)->getChoicesForValues($values);
80+
}
81+
82+
protected function doLoadValuesForChoices(array $choices): array
83+
{
84+
return $this->loadChoiceList()->getValuesForChoices($choices);
85+
}
86+
}

ChoiceList/Loader/CallbackChoiceLoader.php

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,67 +11,25 @@
1111

1212
namespace Symfony\Component\Form\ChoiceList\Loader;
1313

14-
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
15-
1614
/**
17-
* Loads an {@link ArrayChoiceList} instance from a callable returning an array of choices.
15+
* Loads an {@link ArrayChoiceList} instance from a callable returning iterable choices.
1816
*
1917
* @author Jules Pietri <jules@heahprod.com>
2018
*/
21-
class CallbackChoiceLoader implements ChoiceLoaderInterface
19+
class CallbackChoiceLoader extends AbstractChoiceLoader
2220
{
2321
private $callback F438 ;
2422

2523
/**
26-
* The loaded choice list.
27-
*
28-
* @var ArrayChoiceList
29-
*/
30-
private $choiceList;
31-
32-
/**
33-
* @param callable $callback The callable returning an array of choices
24+
* @param callable $callback The callable returning iterable choices
3425
*/
3526
public function __construct(callable $callback)
3627
{
3728
$this->callback = $callback;
3829
}
3930

40-
/**
41-
* {@inheritdoc}
42-
*/
43-
public function loadChoiceList(callable $value = null)
31+
protected function loadChoices(): iterable
4432
{
45-
if (null !== $this->choiceList) {
46-
return $this->choiceList;
47-
}
48-
49-
return $this->choiceList = new ArrayChoiceList(($this->callback)(), $value);
50-
}
51-
52-
/**
53-
* {@inheritdoc}
54-
*/
55-
public function loadChoicesForValues(array $values, callable $value = null)
56-
{
57-
// Optimize
58-
if (empty($values)) {
59-
return [];
60-
}
61-
62-
return $this->loadChoiceList($value)->getChoicesForValues($values);
63-
}
64-
65-
/**
66-
* {@inheritdoc}
67-
*/
68-
public function loadValuesForChoices(array $choices, callable $value = null)
69-
{
70-
// Optimize
71-
if (empty($choices)) {
72-
return [];
73-
}
74-
75-
return $this->loadChoiceList($value)->getValuesForChoices($choices);
33+
return ($this->callback)();
7634
}
7735
}

ChoiceList/Loader/IntlCallbackChoiceLoader.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,21 @@ class IntlCallbackChoiceLoader extends CallbackChoiceLoader
2424
*/
2525
public function loadChoicesForValues(array $values, callable $value = null)
2626
{
27-
// Optimize
28-
$values = array_filter($values);
29-
if (empty($values)) {
30-
return [];
31-
}
32-
33-
return $this->loadChoiceList($value)->getChoicesForValues($values);
27+
return parent::loadChoicesForValues(array_filter($values), $value);
3428
}
3529

3630
/**
3731
* {@inheritdoc}
3832
*/
3933
public function loadValuesForChoices(array $choices, callable $value = null)
4034
{
41-
// Optimize
4235
$choices = array_filter($choices);
43-
if (empty($choices)) {
44-
return [];
45-
}
4636

4737
// If no callable is set, choices are the same as values
4838
if (null === $value) {
4939
return $choices;
5040
}
5141

52-
return $this->loadChoiceList($value)->getValuesForChoices($choices);
42+
return parent::loadValuesForChoices($choices, $value);
5343
}
5444
}

0 commit comments

Comments
 (0)
0