-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] added CallbackChoiceLoader
and refactored ChoiceType's children
#18332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Form] added CallbackChoiceLoader
and refactored ChoiceType's children
#18332
Conversation
{ | ||
return 'currency'; | ||
if (null === self::$currencies) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
storing them in a static property will increase the memory usage by preventing garbage collection until the end of the process.
What kind of performance gain is there in term of time here, to counter-balance the loss regarding memory perf ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those types may be used many times in a form or with many forms in one request.
This optimisation is already done in TimeZoneType
.
I have no hard feeling about this, just a PoC for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure they can be used many times. But any such optimization like this must be backed by numbers (especially when it is expected to improve one side at the expense of a loss on the other side, to know whether the loss can be accepted)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
anyway, I would suggest splitting this caching outside this PR, to discuss it separately (this caching can even be applied without the refactoring done in this PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Thanks @stof for the review!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4caba21
to
dff2883
Compare
choice_filter
option to ChoiceType
dff2883
to
d3f6767
Compare
6360f60
to
fbda538
Compare
Ok so I dropped 8298b1d because the I still think this abstract class is good for DX and maintainability. For example introducing the This may be trivial but there is no harm in performance, I've tried this code in SE: $builder = $this->createFormBuilder();
for ($i = 0; $i < 10; ++$i) {
$builder->add('country'.$i, FormType\CountryType::class);
$builder->add('currency'.$i, FormType\CurrencyType::class);
$builder->add('language'.$i, FormType\LanguageType::class);
$builder->add('locale'.$i, FormType\LocaleType::class);
$builder->add('timezone'.$i, FormType\TimezoneType::class);
}
$form = $builder->getForm(); From base to 8298b1d (try to improve cache with static properties: |
ping @webmozart, does it fit with #18368 ? |
A simpler and more flexible solution would be to add a class CountryType extends AbstractType
{
private $choiceLoader;
public function __construct()
{
$this->choiceLoader = new CallableChoiceLoader(function () {
return array_flip(Intl::getRegionBundle()->getCountryNames());
});
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choice_loader' => $this->choiceLoader,
]);
}
} For each type, we make sure to reuse the same loader instance, then the caching mechanisms in |
I thought about it too since it is very easy to do, I'm glad you brought that up, I might be done in time :) Should I go on with it ? |
I prefer the solution suggested by @webmozart |
fbda538
to
3f8525f
Compare
Rebased. I first thought of using I think this callable choice loader will really improve performances, I will try to profile something today :) Also, I still think of some things:
Concerning choice loaders in general, if this PR is merged, this would be the first one implemented in the form core and in regard of the work I'm trying to do in #18359, I think there is two much caching when using loaders here's a schema:
The So I suggest to use the following code in
And together with the change of #18359 we can get in 4.0:
I think the loader should not know about factories. The What do you think ? Thanks. |
public function loadChoicesForValues(array $values, $value = null) | ||
{ | ||
// Optimize | ||
if (empty($value)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$values
? Is this not tested?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The choice loader should not be responsible for $value
check or test IMHO.
3f8525f
to
3aa5442
Compare
When exactly will 3.1 be feature frozen ? |
* @var ArrayChoiceList | ||
*/ | ||
private $choiceList; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add documentation about what the callable is expected to return (namely an array of choices)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@HeahDude You seemed to agree, but I don't see any documentation here. Can you add a phpdoc? I would then remove the doc on the property above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fabpot Sorry, I don't understand where you want me to add and remove things.
Back then I had changed the first phpdoc from The callable used to load the choices.
to The callable used to load the array of choices.
thinking that both phpdocs would be less confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was not very clear. Let me try again. It would be better to add a phpdoc to the constructor:
/**
* @param callable $callback The callable used to load the array of choices
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}
and remove the one on the private property:
private $callback;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, makes sense. I will do it very soon.
8e8bf19
to
64d1d9c
Compare
@stof All comments addressed here, plus the optimization by implementing the This one still has a chance. I'm just waiting for the appveyor tests to be green :) |
64d1d9c
to
d8539aa
Compare
Green :) |
I will try to profile again but there's no doubt that they'll be better than above :) |
/** | ||
* @author Jules Pietri <jules@heahprod.com> | ||
*/ | ||
abstract class AbstractChoiceLoaderTest extends \PHPUnit_Framework_TestCase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, there is no need to introduce this abstract class, as you use it only once
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you think it might help to introduce other loaders in the future and test them ?
I did it because it's the first implementation in the form component (the only one before was the DoctrineChoiceLoader
) but this part should be re-usable IMHO.
Of courses I can easily integrate it in CallbackChoiceLoaderTest
class.
Is this the final blocker ? Thank you again for your review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@HeahDude We can still extract common logic in an abstract base class then if needed. I would remove this class for now too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough! I do it right away.
d8539aa
to
8e3b279
Compare
Alright, last comments addressed! Failures are unrelated, I think I'm done here :) |
👍 |
8e3b279
to
ec0fa1f
Compare
Rebased. Tests still green. Last comment here is @fabpot approval. Is this still planned for 3.1? If so, it should be merged as soon as possible to be tested during beta. ping @symfony/deciders Thanks. |
8607c56
to
72173aa
Compare
Re-re-rebased :) |
72173aa
to
8a4e164
Compare
Comments addressed and changelog updated to target 3.2. |
@HeahDude Can you submit a PR for the docs? Thanks. |
Thank you @HeahDude. |
…iceType's children (HeahDude) This PR was merged into the 3.2-dev branch. Discussion ---------- [Form] added `CallbackChoiceLoader` and refactored ChoiceType's children | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | ~ | License | MIT | Doc PR | todo Todo ==== - [ ] Address a doc PR - [x] Update CHANGELOG Changes ======= - 39e937f added `CallbackChoiceLoader` to lazy load choices with a simple callable. - 995dc56 refactored `CountryType`, `CurrencyType`, `LanguageType`, `LocaleType` and `TimezoneType` for better performance by implementing `ChoiceLoaderInterface` for lazy loading. Usage ===== ```php use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; $builder->add('constants', ChoiceType::class, array( 'choice_loader' => new CallbackChoiceLoader(function() { return StaticClass::getConstants(); }, )); ``` Commits ------- 8a4e164 [Form] implemented ChoiceLoaderInterface in children of ChoiceType afd7bf8 [Form] added `CallbackChoiceLoader`
Will do, thank you @fabpot |
Todo
Changes
CallbackChoiceLoader
to lazy load choices with a simple callable.CountryType
,CurrencyType
,LanguageType
,LocaleType
andTimezoneType
for better performance by implementingChoiceLoaderInterface
for lazy loading.Usage