Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

[Form] addedCallbackChoiceLoader 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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

@HeahDude
Copy link
Contributor

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed tickets~
LicenseMIT
Doc PRtodo

Todo

  • Address a doc PR
  • Update CHANGELOG

Changes

  • 39e937f addedCallbackChoiceLoader to lazy load choices with a simple callable.
  • 995dc56 refactoredCountryType,CurrencyType,LanguageType,LocaleType andTimezoneType for better performance by implementingChoiceLoaderInterface for lazy loading.

Usage

useSymfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;useSymfony\Component\Form\Extension\Core\Type\ChoiceType;$builder->add('constants', ChoiceType::class,array('choice_loader' =>newCallbackChoiceLoader(function() {return StaticClass::getConstants();    },));

protectedfunctionloadChoices()
{
return'currency';
if (null ===self::$currencies) {
Copy link
Member

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 ?

Copy link
ContributorAuthor

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 inTimeZoneType.

I have no hard feeling about this, just a PoC for now.

Copy link
Member

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)

Copy link
Member

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)

Copy link
ContributorAuthor

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!

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@stof this PR is now about the caching in a first commit and a refactoring in a second commit.

Forchoice_filter and a better approach in the implementation see#18334.

I would very much appreciate some feedback there before adding more tests. Thanks.

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch 5 times, most recently from4caba21 todff2883CompareMarch 28, 2016 04:33
@HeahDudeHeahDude changed the title[Form] add ImmutableChoiceType andchoice_filter option toChoiceType[Form] optimize ChoiceType children cache and add ImmutableChoiceTypeMar 28, 2016
@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch fromdff2883 tod3f6767CompareMarch 28, 2016 04:45
@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch 3 times, most recently from6360f60 tofbda538CompareMarch 29, 2016 07:05
@HeahDudeHeahDude changed the title[Form] optimize ChoiceType children cache and add ImmutableChoiceType[Form] add ImmutableChoiceTypeMar 29, 2016
@HeahDude
Copy link
ContributorAuthor

Ok so I dropped8298b1d because theCachingFactoryDecorator (ref#16747) already do the job, then there is no gain in caching thearray_flip even if it is justified forTimezoneType.

I still think this abstract class is good for DX and maintainability. For example introducing thechoice_translation_domain option would have required one line to this abstract class instead of modifying 5 classes.
And extendingImmutableChoiceType to only implementloadChoices is better for DX than implementingChoiceLoaderInterface.

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 to8298b1d (try to improve cache with static properties:
10_form_base_to_cache

And from base to refactoring (current PR):
10_from_base_to_refactor

@HeahDude
Copy link
ContributorAuthor

ping@webmozart, does it fit with#18368 ?

@webmozart
Copy link
Contributor

A simpler and more flexible solution would be to add aCallableChoiceLoader. To the constructor of that loader, we can pass a callable in each of these types that loads the choices, e.g.:

class CountryTypeextends AbstractType{private$choiceLoader;publicfunction__construct()    {$this->choiceLoader =newCallableChoiceLoader(function () {returnarray_flip(Intl::getRegionBundle()->getCountryNames());        });    }publicfunctionconfigureOptions(OptionsResolver$resolver)    {$resolver->setDefaults(['choice_loader' =>$this->choiceLoader,        ]);    }}

For each type, we make sure to reuse the same loader instance, then the caching mechanisms inChoiceType will automatically take effect.

zemd, stof, Valantir007, and rvdbogerd reacted with thumbs up emoji

@HeahDude
Copy link
ContributorAuthor

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 ?

webmozart reacted with thumbs up emoji

@stof
Copy link
Member

I prefer the solution suggested by@webmozart

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch fromfbda538 to3f8525fCompareMarch 31, 2016 04:49
@HeahDude
Copy link
ContributorAuthor

Rebased.

I first thought of usingchoice_loader as any callable when dealing with#18334, but I was not sure that it would be good to add an interface for that, although that's looks very great for those sub types!

I think this callable choice loader will really improve performances, I will try to profile something today :)

Also, I still think of some things:

  • it would be good to decouple the need to inherit from choice type basis from the need to load choices in a permanent way to ease maintaining them.

    This was originally sent as PoC, but it would be good to find a better name for such an abstract class:
    LoadedChoiceType orAbstractLazyChoiceType, it would be a more generic core version ofDoctrineType.

  • I feelTimezoneType is looking really good now but the four others could be more optimised soon (in regard ofSimplify access to CLDR/ICU data #18368) to directly implement theChoiceLoaderInterface as we can do for mappers or transformers (e.g[Form] LetTextType implementDataTransformerInterface #18357).

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:

Needs choice list (configuration | loading form types)|=> factory   |   |__ from choices ?   |   |   |   |__ decorated with cache ?   |   |   |         |   |   |__ => return ArrayChoiceList   |   |   |   |   |   |__ decorates   |   |   |   |__ => creates   |       |   |       |__ => cache => return ArrayChoiceList    |   |   |__ from loader ?       |       |__ decorated with cache ?           |             |__ => return ArrayChoiceList           |           |           |__ decorates               |               |__ => cache (*1) and return **LazyChoiceList**Then later or not uses choice list (actually build form or view)|=> not lazy ?    |    |__ => return classic (good)    |    |__ is loaded ?        |        |__ => return classic        |        |__ call loader            |            |__ is loaded ?            |   |            |   |__ => return classic            |   |            |   |__ call factory            |   |   |            |   |   |__ decorated with cache            |   |   |    |            |   |   |    |__ wtf ?            |   |   |            |   |   |__ decorated with property path ?            |   |   |    |            |   |   |    |__ wtf ?            |   |   |            |   |   |__ factory creates, cache (*2) and return ArrayChoiceList            |   |            |   |__ loader cache (*3)            |            |__ lazy choice list cache (*4)

The(*n) marker indicates when the caching implies the number of copy of the same choice list.

So I suggest to use the following code inLazyChoiceList:

    public function getChoices()    {        // BC        if (!$this->isLoaded) {            $this->loadedList = $this->loader->loadChoiceList($this->value);            $this->isLoaded = true;        } else {            if ($this->loadedList !== $loadedList = $this->loader->loadChoiceList($this->value) {                // Trigger deprecation notice for caching in lazy choice list                // not caching in choice loader unsupported in 4.0                return $this->loadedList->getChoices();            }        }        // End bc        return $this->loader->loadChoiceList($this->value)->getChoices();    }

And together with the change of#18359 we can get in 4.0:

Uses choice list (actually build form or view)|=> not lazy ?    |    |__ => return classic (good)    |    |    |__ call loader        |        |__ is loaded ?            |            |__ => return classic            |            |__ loader creates, cache (*2) and returns ChoiceListInterface

I think the loader should not know about factories. TheDoctrineChoiceLoader is not registered as service, it is only instantiated by default in theDoctrineType which passes it the factory so it's not really an extension point.

What do you think ?

Thanks.

public function loadChoicesForValues(array $values, $value = null)
{
// Optimize
if (empty($value)) {
Copy link
Contributor

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?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Typo...

Copy link
ContributorAuthor

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.

@HeahDude
Copy link
ContributorAuthor

When exactly will 3.1 be feature frozen ?

* @var ArrayChoiceList
*/
private $choiceList;

Copy link
Member

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)

HeahDude reacted with thumbs up emoji
Copy link
Member

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.

Copy link
ContributorAuthor

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 fromThe callable used to load the choices. toThe callable used to load the array of choices. thinking that both phpdocs would be less confusing.

Copy link
Member

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  */publicfunction__construct(callable$callback) {$this->callback =$callback;}

and remove the one on the private property:

private$callback;

Copy link
ContributorAuthor

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.

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch 2 times, most recently from8e8bf19 to64d1d9cCompareApril 7, 2016 15:59
@HeahDude
Copy link
ContributorAuthor

@stof All comments addressed here, plus the optimization by implementing theChoiceLoaderInterface directly!

This one still has a chance. I'm just waiting for the appveyor tests to be green :)

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch from64d1d9c tod8539aaCompareApril 7, 2016 16:02
@HeahDude
Copy link
ContributorAuthor

Green :)

@HeahDude
Copy link
ContributorAuthor

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
Copy link
Member

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

Copy link
ContributorAuthor

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 theDoctrineChoiceLoader) but this part should be re-usable IMHO.

Of courses I can easily integrate it inCallbackChoiceLoaderTest class.

Is this the final blocker ? Thank you again for your review.

Copy link
Member

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.

Copy link
ContributorAuthor

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.

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch fromd8539aa to8e3b279CompareApril 11, 2016 10:11
@HeahDude
Copy link
ContributorAuthor

Alright, last comments addressed! Failures are unrelated, I think I'm done here :)

@fabpot
Copy link
Member

👍

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch from8e3b279 toec0fa1fCompareApril 25, 2016 13:45
@HeahDude
Copy link
ContributorAuthor

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.

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch 2 times, most recently from8607c56 to72173aaCompareApril 30, 2016 06:15
@HeahDude
Copy link
ContributorAuthor

Re-re-rebased :)

@HeahDudeHeahDudeforce-pushed thefeature-form-immutable_choice_type branch from72173aa to8a4e164CompareJune 13, 2016 10:05
@HeahDude
Copy link
ContributorAuthor

Comments addressed and changelog updated to target 3.2.

@fabpot
Copy link
Member

@HeahDude Can you submit a PR for the docs? Thanks.

@fabpot
Copy link
Member

Thank you@HeahDude.

@fabpotfabpot merged commit8a4e164 intosymfony:masterJun 13, 2016
fabpot added a commit that referenced this pull requestJun 13, 2016
…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        | todoTodo====- [ ] Address a doc PR- [x] Update CHANGELOGChanges======= -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=====```phpuse 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 ChoiceTypeafd7bf8 [Form] added `CallbackChoiceLoader`
@HeahDude
Copy link
ContributorAuthor

Will do, thank you@fabpot

@HeahDudeHeahDude deleted the feature-form-immutable_choice_type branchJune 13, 2016 12:06
@fabpotfabpot mentioned this pull requestOct 27, 2016
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

7 participants

@HeahDude@webmozart@stof@fabpot@xabbuh@javiereguiluz@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp