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

Commitd73b5ee

Browse files
committed
add LazyChoiceLoader and choice_lazy option
1 parentc3bb47a commitd73b5ee

File tree

8 files changed

+374
-16
lines changed

8 files changed

+374
-16
lines changed

‎src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php‎

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
useSymfony\Bridge\Doctrine\Tests\Fixtures\SingleStringCastableIdEntity;
3131
useSymfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity;
3232
useSymfony\Component\Form\ChoiceList\LazyChoiceList;
33+
useSymfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader;
3334
useSymfony\Component\Form\ChoiceList\View\ChoiceGroupView;
3435
useSymfony\Component\Form\ChoiceList\View\ChoiceView;
3536
useSymfony\Component\Form\Exception\RuntimeException;
@@ -1758,4 +1759,128 @@ public function testWithSameLoaderAndDifferentChoiceValueCallbacks()
17581759
$this->assertSame('Foo',$view['entity_two']->vars['choices']['Foo']->value);
17591760
$this->assertSame('Bar',$view['entity_two']->vars['choices']['Bar']->value);
17601761
}
1762+
1763+
publicfunctiontestEmptyChoicesWhenLazy()
1764+
{
1765+
if (!class_exists(LazyChoiceLoader::class)) {
1766+
$this->markTestSkipped('This test requires symfony/form 7.2 or superior.');
1767+
}
1768+
1769+
$entity1 =newSingleIntIdEntity(1,'Foo');
1770+
$entity2 =newSingleIntIdEntity(2,'Bar');
1771+
$this->persist([$entity1,$entity2]);
1772+
1773+
$view =$this->factory->create(FormTypeTest::TESTED_TYPE)
1774+
->add('entity_one',self::TESTED_TYPE, [
1775+
'em' =>'default',
1776+
'class' =>self::SINGLE_IDENT_CLASS,
1777+
'choice_lazy' =>true,
1778+
])
1779+
->createView()
1780+
;
1781+
1782+
$this->assertCount(0,$view['entity_one']->vars['choices']);
1783+
}
1784+
1785+
publicfunctiontestLoadedChoicesWhenLazyAndBoundData()
1786+
{
1787+
if (!class_exists(LazyChoiceLoader::class)) {
1788+
$this->markTestSkipped('This test requires symfony/form 7.2 or superior.');
1789+
}
1790+
1791+
$entity1 =newSingleIntIdEntity(1,'Foo');
1792+
$entity2 =newSingleIntIdEntity(2,'Bar');
1793+
$this->persist([$entity1,$entity2]);
1794+
1795+
$view =$this->factory->create(FormTypeTest::TESTED_TYPE, ['entity_one' =>$entity1])
1796+
->add('entity_one',self::TESTED_TYPE, [
1797+
'em' =>'default',
1798+
'class' =>self::SINGLE_IDENT_CLASS,
1799+
'choice_lazy' =>true,
1800+
])
1801+
->createView()
1802+
;
1803+
1804+
$this->assertCount(1,$view['entity_one']->vars['choices']);
1805+
$this->assertSame('1',$view['entity_one']->vars['choices'][1]->value);
1806+
}
1807+
1808+
publicfunctiontestLoadedChoicesWhenLazyAndSubmittedData()
1809+
{
1810+
if (!class_exists(LazyChoiceLoader::class)) {
1811+
$this->markTestSkipped('This test requires symfony/form 7.2 or superior.');
1812+
}
1813+
1814+
$entity1 =newSingleIntIdEntity(1,'Foo');
1815+
$entity2 =newSingleIntIdEntity(2,'Bar');
1816+
$this->persist([$entity1,$entity2]);
1817+
1818+
$view =$this->factory->create(FormTypeTest::TESTED_TYPE)
1819+
->add('entity_one',self::TESTED_TYPE, [
1820+
'em' =>'default',
1821+
'class' =>self::SINGLE_IDENT_CLASS,
1822+
'choice_lazy' =>true,
1823+
])
1824+
->submit(['entity_one' =>'2'])
1825+
->createView()
1826+
;
1827+
1828+
$this->assertCount(1,$view['entity_one']->vars['choices']);
1829+
$this->assertSame('2',$view['entity_one']->vars['choices'][2]->value);
1830+
}
1831+
1832+
publicfunctiontestEmptyChoicesWhenLazyAndEmptyDataIsSubmitted()
1833+
{
1834+
if (!class_exists(LazyChoiceLoader::class)) {
1835+
$this->markTestSkipped('This test requires symfony/form 7.2 or superior.');
1836+
}
1837+
1838+
$entity1 =newSingleIntIdEntity(1,'Foo');
1839+
$entity2 =newSingleIntIdEntity(2,'Bar');
1840+
$this->persist([$entity1,$entity2]);
1841+
1842+
$view =$this->factory->create(FormTypeTest::TESTED_TYPE, ['entity_one' =>$entity1])
1843+
->add('entity_one',self::TESTED_TYPE, [
1844+
'em' =>'default',
1845+
'class' =>self::SINGLE_IDENT_CLASS,
1846+
'choice_lazy' =>true,
1847+
])
1848+
->submit([])
1849+
->createView()
1850+
;
1851+
1852+
$this->assertCount(0,$view['entity_one']->vars['choices']);
1853+
}
1854+
1855+
publicfunctiontestErrorOnSubmitInvalidValuesWhenLazyAndCustomQueryBuilder()
1856+
{
1857+
if (!class_exists(LazyChoiceLoader::class)) {
1858+
$this->markTestSkipped('This test requires symfony/form 7.2 or superior.');
1859+
}
1860+
1861+
$entity1 =newSingleIntIdEntity(1,'Foo');
1862+
$entity2 =newSingleIntIdEntity(2,'Bar');
1863+
$this->persist([$entity1,$entity2]);
1864+
$qb =$this->em
1865+
->createQueryBuilder()
1866+
->select('e')
1867+
->from(self::SINGLE_IDENT_CLASS,'e')
1868+
->where('e.id = 2')
1869+
;
1870+
1871+
$form =$this->factory->create(FormTypeTest::TESTED_TYPE, ['entity_one' =>$entity2])
1872+
->add('entity_one',self::TESTED_TYPE, [
1873+
'em' =>'default',
1874+
'class' =>self::SINGLE_IDENT_CLASS,
1875+
'query_builder' =>$qb,
1876+
'choice_lazy' =>true,
1877+
])
1878+
->submit(['entity_one' =>'1'])
1879+
;
1880+
$view =$form->createView();
1881+
1882+
$this->assertCount(0,$view['entity_one']->vars['choices']);
1883+
$this->assertCount(1,$errors =$form->getErrors(true));
1884+
$this->assertSame('The selected choice is invalid.',$errors->current()->getMessage());
1885+
}
17611886
}

‎src/Symfony/Component/Form/CHANGELOG.md‎

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

77
* Deprecate the`VersionAwareTest` trait, use feature detection instead
88
* Add support for the`calendar` option in`DateType`
9+
* Add`LazyChoiceLoader` and`choice_lazy` option in`ChoiceType` for loading and rendering choices on demand
910

1011
7.1
1112
---
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
namespaceSymfony\Component\Form\ChoiceList\Loader;
13+
14+
useSymfony\Component\Form\ChoiceList\ArrayChoiceList;
15+
useSymfony\Component\Form\ChoiceList\ChoiceListInterface;
16+
17+
/**
18+
* A choice loader that loads its choices and values lazily, only when necessary.
19+
*
20+
* @author Yonel Ceruto <yonelceruto@gmail.com>
21+
*/
22+
class LazyChoiceLoaderimplements ChoiceLoaderInterface
23+
{
24+
private ?ChoiceListInterface$choiceList =null;
25+
26+
publicfunction__construct(
27+
privatereadonlyChoiceLoaderInterface$loader,
28+
) {
29+
}
30+
31+
publicfunctionloadChoiceList(?callable$value =null):ChoiceListInterface
32+
{
33+
return$this->choiceList ??=newArrayChoiceList([],$value);
34+
}
35+
36+
publicfunctionloadChoicesForValues(array$values, ?callable$value =null):array
37+
{
38+
$choices =$this->loader->loadChoicesForValues($values,$value);
39+
$this->choiceList =newArrayChoiceList($choices,$value);
40+
41+
return$choices;
42+
}
43+
44+
publicfunctionloadValuesForChoices(array$choices, ?callable$value =null):array
45+
{
46+
$values =$this->loader->loadValuesForChoices($choices,$value);
47+
48+
if ($this->choiceList?->getValuesForChoices($choices) !==$values) {
49+
$this->loadChoicesForValues($values,$value);
50+
}
51+
52+
return$values;
53+
}
54+
}

‎src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
useSymfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
2828
useSymfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
2929
useSymfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
30+
useSymfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader;
3031
useSymfony\Component\Form\ChoiceList\View\ChoiceGroupView;
3132
useSymfony\Component\Form\ChoiceList\View\ChoiceListView;
3233
useSymfony\Component\Form\ChoiceList\View\ChoiceView;
3334
useSymfony\Component\Form\Event\PreSubmitEvent;
35+
useSymfony\Component\Form\Exception\LogicException;
3436
useSymfony\Component\Form\Exception\TransformationFailedException;
3537
useSymfony\Component\Form\Extension\Core\DataMapper\CheckboxListMapper;
3638
useSymfony\Component\Form\Extension\Core\DataMapper\RadioListMapper;
@@ -333,11 +335,24 @@ public function configureOptions(OptionsResolver $resolver): void
333335
return$choiceTranslationDomain;
334336
};
335337

338+
$choiceLoaderNormalizer =staticfunction (Options$options, ?ChoiceLoaderInterface$choiceLoader) {
339+
if (!$options['choice_lazy']) {
340+
return$choiceLoader;
341+
}
342+
343+
if (null ===$choiceLoader) {
344+
thrownewLogicException('The "choice_lazy" option can only be used if the "choice_loader" option is set.');
345+
}
346+
347+
returnnewLazyChoiceLoader($choiceLoader);
348+
};
349+
336350
$resolver->setDefaults([
337351
'multiple' =>false,
338352
'expanded' =>false,
339353
'choices' => [],
340354
'choice_filter' =>null,
355+
'choice_lazy' =>false,
341356
'choice_loader' =>null,
342357
'choice_label' =>null,
343358
'choice_name' =>null,
@@ -365,9 +380,11 @@ public function configureOptions(OptionsResolver $resolver): void
365380

366381
$resolver->setNormalizer('placeholder',$placeholderNormalizer);
367382
$resolver->setNormalizer('choice_translation_domain',$choiceTranslationDomainNormalizer);
383+
$resolver->setNormalizer('choice_loader',$choiceLoaderNormalizer);
368384

369385
$resolver->setAllowedTypes('choices', ['null','array', \Traversable::class]);
370386
$resolver->setAllowedTypes('choice_translation_domain', ['null','bool','string']);
387+
$resolver->setAllowedTypes('choice_lazy','bool');
371388
$resolver->setAllowedTypes('choice_loader', ['null', ChoiceLoaderInterface::class, ChoiceLoader::class]);
372389
$resolver->setAllowedTypes('choice_filter', ['null','callable','string', PropertyPath::class, ChoiceFilter::class]);
373390
$resolver->setAllowedTypes('choice_label', ['null','bool','callable','string', PropertyPath::class, ChoiceLabel::class]);
@@ -381,6 +398,8 @@ public function configureOptions(OptionsResolver $resolver): void
381398
$resolver->setAllowedTypes('separator_html', ['bool']);
382399
$resolver->setAllowedTypes('duplicate_preferred_choices','bool');
383400
$resolver->setAllowedTypes('group_by', ['null','callable','string', PropertyPath::class, GroupBy::class]);
401+
402+
$resolver->setInfo('choice_lazy','Load choices on demand. When set to true, only the selected choices are loaded and rendered.');
384403
}
385404

386405
publicfunctiongetBlockPrefix():string
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
namespaceSymfony\Component\Form\Tests\ChoiceList\Loader;
13+
14+
usePHPUnit\Framework\TestCase;
15+
useSymfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader;
16+
useSymfony\Component\Form\Tests\Fixtures\ArrayChoiceLoader;
17+
18+
class LazyChoiceLoaderTestextends TestCase
19+
{
20+
privateLazyChoiceLoader$loader;
21+
22+
protectedfunctionsetUp():void
23+
{
24+
$this->loader =newLazyChoiceLoader(newArrayChoiceLoader(['A','B','C']));
25+
}
26+
27+
publicfunctiontestInitialEmptyChoiceListLoading()
28+
{
29+
$this->assertSame([],$this->loader->loadChoiceList()->getChoices());
30+
}
31+
32+
publicfunctiontestOnDemandChoiceListAfterLoadingValuesForChoices()
33+
{
34+
$this->loader->loadValuesForChoices(['A']);
35+
$this->assertSame(['A' =>'A'],$this->loader->loadChoiceList()->getChoices());
36+
}
37+
38+
publicfunctiontestOnDemandChoiceListAfterLoadingChoicesForValues()
39+
{
40+
$this->loader->loadChoicesForValues(['B']);
41+
$this->assertSame(['B' =>'B'],$this->loader->loadChoiceList()->getChoices());
42+
}
43+
44+
publicfunctiontestOnDemandChoiceList()
45+
{
46+
$this->loader->loadValuesForChoices(['A']);
47+
$this->loader->loadChoicesForValues(['B']);
48+
$this->assertSame(['B' =>'B'],$this->loader->loadChoiceList()->getChoices());
49+
}
50+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp