diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index ed8e7a5c3c85f..99f3019726964 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -94,12 +94,12 @@ public static function createChoiceName($choice, $key, $value) * Gets important parts from QueryBuilder that will allow to cache its results. * For instance in ORM two query builders with an equal SQL string and * equal parameters are considered to be equal. - * + * * @param object $queryBuilder - * + * * @return array|false Array with important QueryBuilder parts or false if * they can't be determined - * + * * @internal This method is public to be usable as callback. It should not * be used in user code. */ @@ -111,7 +111,12 @@ public function getQueryBuilderPartsForCachingHash($queryBuilder) public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null) { $this->registry = $registry; - $this->choiceListFactory = $choiceListFactory ?: new PropertyAccessDecorator(new DefaultChoiceListFactory(), $propertyAccessor); + $this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator( + new PropertyAccessDecorator( + new DefaultChoiceListFactory(), + $propertyAccessor + ) + ); } public function buildForm(FormBuilderInterface $builder, array $options) diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php index 486eca94abf77..fef097d3ea60d 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Form\Type; use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; use Symfony\Component\Form\Exception\UnexpectedTypeException; @@ -64,19 +65,31 @@ public function getName() /** * We consider two query builders with an equal SQL string and * equal parameters to be equal. - * + * * @param QueryBuilder $queryBuilder - * + * * @return array - * + * * @internal This method is public to be usable as callback. It should not * be used in user code. */ public function getQueryBuilderPartsForCachingHash($queryBuilder) { return array( - $queryBuilder->getQuery()->getSQL(), - $queryBuilder->getParameters()->toArray(), + $queryBuilder->getQuery()->getSQL(), + array_map(array($this, 'parameterToArray'), $queryBuilder->getParameters()->toArray()), ); } + + /** + * Converts a query parameter to an array. + * + * @param Parameter $parameter The query parameter + * + * @return array The array representation of the parameter + */ + private function parameterToArray(Parameter $parameter) + { + return array($parameter->getName(), $parameter->getType(), $parameter->getValue()); + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index ce37d261c06e3..c5cdc60bbd342 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -1114,6 +1114,69 @@ public function testLoaderCaching() $this->assertSame($choiceList1, $choiceList3); } + public function testLoaderCachingWithParameters() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $repo = $this->em->getRepository(self::SINGLE_IDENT_CLASS); + + $entityType = new EntityType( + $this->emRegistry, + PropertyAccess::createPropertyAccessor() + ); + + $entityTypeGuesser = new DoctrineOrmTypeGuesser($this->emRegistry); + + $factory = Forms::createFormFactoryBuilder() + ->addType($entityType) + ->addTypeGuesser($entityTypeGuesser) + ->getFormFactory(); + + $formBuilder = $factory->createNamedBuilder('form', 'form'); + + $formBuilder->add('property1', 'entity', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1), + )); + + $formBuilder->add('property2', 'entity', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1); + }, + )); + + $formBuilder->add('property3', 'entity', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1); + }, + )); + + $form = $formBuilder->getForm(); + + $form->submit(array( + 'property1' => 1, + 'property2' => 1, + 'property3' => 2, + )); + + $choiceList1 = $form->get('property1')->getConfig()->getOption('choice_list'); + $choiceList2 = $form->get('property2')->getConfig()->getOption('choice_list'); + $choiceList3 = $form->get('property3')->getConfig()->getOption('choice_list'); + + $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\ChoiceListInterface', $choiceList1); + $this->assertSame($choiceList1, $choiceList2); + $this->assertSame($choiceList1, $choiceList3); + } + public function testCacheChoiceLists() { $entity1 = new SingleIntIdEntity(1, 'Foo'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 7d1a588fbad34..2c8667558953b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -57,6 +57,19 @@ + + + + + + + + + + + + + @@ -69,6 +82,7 @@ + diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 1465551b906a6..93492fdf5061a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; use Symfony\Component\Form\ChoiceList\LegacyChoiceListAdapter; use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; @@ -46,7 +47,11 @@ class ChoiceType extends AbstractType public function __construct(ChoiceListFactoryInterface $choiceListFactory = null) { - $this->choiceListFactory = $choiceListFactory ?: new PropertyAccessDecorator(new DefaultChoiceListFactory()); + $this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator( + new PropertyAccessDecorator( + new DefaultChoiceListFactory() + ) + ); } /**