8000 [DoctrineBridge][Form] Fix BC break in DoctrineType · xabbuh/symfony@3172d73 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3172d73

Browse files
malarzmfabpot
authored andcommitted
[DoctrineBridge][Form] Fix BC break in DoctrineType
1 parent e60f715 commit 3172d73

File tree

2 files changed

+86
-28
lines changed

2 files changed

+86
-28
lines changed

src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php

Lines changed: 42 additions & 28 deletions
144
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
use Doctrine\Common\Persistence\ManagerRegistry;
1515
use Doctrine\Common\Persistence\ObjectManager;
16-
use Doctrine\ORM\QueryBuilder;
1716
use Symfony\Bridge\ 8000 Doctrine\Form\ChoiceList\DoctrineChoiceLoader;
1817
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface;
1918
use Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader;
@@ -25,7 +24,6 @@
2524
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
2625
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
2726
use Symfony\Component\Form\Exception\RuntimeException;
28-
use Symfony\Component\Form\Exception\UnexpectedTypeException;
2927
use Symfony\Component\Form\FormBuilderInterface;
3028
use Symfony\Component\OptionsResolver\Options;
3129
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -92,6 +90,24 @@ public static function createChoiceName($choice, $key, $value)
9290
return (string) $value;
9391
}
9492

93+
/**
94+
* Gets important parts from QueryBuilder that will allow to cache its results.
95+
* For instance in ORM two query builders with an equal SQL string and
96+
* equal parameters are considered to be equal.
97+
*
98+
* @param object $queryBuilder
99+
*
100+
* @return array|false Array with important QueryBuilder parts or false if
101+
* they can't be determined
102+
*
103+
* @internal This method is public to be usable as callback. It should not
104+
* be used in user code.
105+
*/
106+
public function getQueryBuilderPartsForCachingHash($queryBuilder)
107+
{
108+
return false;
109+
}
110+
95111
public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null)
96112
{
97113
$this->registry = $registry;
@@ -117,29 +133,28 @@ public function configureOptions(OptionsResolver $resolver)
117133
$type = $this;
118134

119135
$choiceLoader = function (Options $options) use ($choiceListFactory, &$choiceLoaders, $type) {
120-
// This closure and the "query_builder" options should be pushed to
121-
// EntityType in Symfony 3.0 as they are specific to the ORM
122136

123137
// Unless the choices are given explicitly, load them on demand
124138
if (null === $options['choices']) {
125-
// We consider two query builders with an equal SQL string and
126-
// equal parameters to be equal
127-
$qbParts = $options['query_builder']
128-
? array(
129-
$options['query_builder']->getQuery()->getSQL(),
130-
$options['query_builder']->getParameters()->toArray(),
131-
)
132-
: null;
133-
134-
$hash = CachingFactoryDecorator::generateHash(array(
135-
$options['em'],
136-
$options['class'],
137-
$qbParts,
138-
$options['loader'],
139-
));
140139

141-
if (isset($choiceLoaders[$hash])) {
142-
return $choiceLoaders[$hash];
140+
$hash = null;
141+
$qbParts = null;
142+
143+
// If there is no QueryBuilder we can safely cache DoctrineChoiceLoader,
+
// also if concrete Type can return important QueryBuilder parts to generate
145+
// hash key we go for it as well
146+
if (!$options['query_builder'] || false !== ($qbParts = $type->getQueryBuilderPartsForCachingHash($options['query_builder']))) {
147+
148+
$hash = CachingFactoryDecorator::generateHash(array(
149+
$options['em'],
150+
$options['class'],
151+
$qbParts,
152+
$options['loader'],
153+
));
154+
155+
if (isset($choiceLoaders[$hash])) {
156+
return $choiceLoaders[$hash];
157+
}
143158
}
144159

145160
if ($options['loader']) {
@@ -151,15 +166,19 @@ public function configureOptions(OptionsResolver $resolver)
151166
$entityLoader = $type->getLoader($options['em'], $queryBuilder, $options['class']);
152167
}
153168

154-
$choiceLoaders[$hash] = new DoctrineChoiceLoader(
169+
$doctrineChoiceLoader = new DoctrineChoiceLoader(
155170
$choiceListFactory,
156171
$options['em'],
157172
$options['class'],
158173
$options['id_reader'],
159174
$entityLoader
160175
);
161176

162-
return $choiceLoaders[$hash];
177+
if ($hash !== null) {
178+
$choiceLoaders[$hash] = $doctrineChoiceLoader;
179+
}
180+
181+
return $doctrineChoiceLoader;
163182
}
164183
};
165184

@@ -240,10 +259,6 @@ public function configureOptions(OptionsResolver $resolver)
240259
$queryBuilderNormalizer = function (Options $options, $queryBuilder) {
241260
if (is_callable($queryBuilder)) {
242261
$queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class']));
243-
244-
if (!$queryBuilder instanceof QueryBuilder) {
245-
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
246-
}
247262
}
248263

249264
return $queryBuilder;
@@ -305,7 +320,6 @@ public function configureOptions(OptionsResolver $resolver)
305320

306321
$resolver->setAllowedTypes('em', array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager'));
307322
$resolver->setAllowedTypes('loader', array('null', 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'));
308-
$resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder'));
309323
}
310324

311325
/**

src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,34 @@
1414
use Doctrine\Common\Persistence\ObjectManager;
1515
use Doctrine\ORM\QueryBuilder;
1616
use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader;
17+
use Symfony\Component\Form\Exception\UnexpectedTypeException;
18+
use Symfony\Component\OptionsResolver\Options;
19+
use Symfony\Component\OptionsResolver\OptionsResolver;
1720

1821
class EntityType extends DoctrineType
1922
{
23+
public function configureOptions(OptionsResolver $resolver)
24+
{
25+
parent::configureOptions($resolver);
26+
27+
// Invoke the query builder closure so that we can cache choice lists
28+
// for equal query builders
29+
$queryBuilderNormalizer = function (Options $options, $queryBuilder) {
30+
if (is_callable($queryBuilder)) {
31+
$queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class']));
32+
33+
if (!$queryBuilder instanceof QueryBuilder) {
34+
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
35+
}
36+
}
37+
38+
return $queryBuilder;
39+
};
40+
41+
$resolver->setNormalizer('query_builder', $queryBuilderNormalizer);
42+
$resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder'));
43+
}
44+
2045
/**
2146
* Return the default loader object.
2247
*
@@ -35,4 +60,23 @@ public function getName()
3560
{
3661
return 'entity';
3762
}
63+
64+
/**
65+
* We consider two query builders with an equal SQL string and
66+
* equal parameters to be equal.
67+
*
68+
* @param QueryBuilder $queryBuilder
69+
*
70+
* @return array
71+
*
72+
* @internal This method is public to be usable as callback. It should not
73+
* be used in user code.
74+
*/
75+
public function getQueryBuilderPartsForCachingHash($queryBuilder)
76+
{
77+
return array(
78+
$queryBuilder->getQuery()->getSQL(),
79+
$queryBuilder->getParameters()->toArray(),
80+
);
81+
}
3882
}

0 commit comments

Comments
 (0)
0