13
13
14
14
use Doctrine \Common \Persistence \ManagerRegistry ;
15
15
use Doctrine \Common \Persistence \ObjectManager ;
16
+ use Doctrine \ORM \QueryBuilder ;
16
17
use Symfony \Bridge \Doctrine \Form \ChoiceList \DoctrineChoiceLoader ;
17
18
use Symfony \Bridge \Doctrine \Form \ChoiceList \EntityLoaderInterface ;
18
19
use Symfony \Bridge \Doctrine \Form \DataTransformer \CollectionToArrayTransformer ;
23
24
use Symfony \Component \Form \ChoiceList \Factory \DefaultChoiceListFactory ;
24
25
use Symfony \Component \Form \ChoiceList \Factory \PropertyAccessDecorator ;
25
26
use Symfony \Component \Form \Exception \RuntimeException ;
27
+ use Symfony \Component \Form \Exception \UnexpectedTypeException ;
26
28
use Symfony \Component \Form \FormBuilderInterface ;
27
29
use Symfony \Component \OptionsResolver \Options ;
28
30
use Symfony \Component \OptionsResolver \OptionsResolver ;
@@ -71,20 +73,24 @@ public function configureOptions(OptionsResolver $resolver)
71
73
$ choiceLoader = function (Options $ options ) use ($ choiceListFactory , &$ choiceLoaders , $ type ) {
72
74
// Unless the choices are given explicitly, load them on demand
73
75
if (null === $ options ['choices ' ]) {
74
- // Don't cache if the query builder is constructed dynamically
75
- if ($ options ['query_builder ' ] instanceof \Closure) {
76
- $ hash = null ;
77
- } else {
78
- $ hash = CachingFactoryDecorator::generateHash (array (
79
- $ options ['em ' ],
80
- $ options ['class ' ],
81
- $ options ['query_builder ' ],
82
- $ options ['loader ' ],
83
- ));
84
-
85
- if (isset ($ choiceLoaders [$ hash ])) {
86
- return $ choiceLoaders [$ hash ];
87
- }
76
+ // We consider two query builders with an equal SQL string and
77
+ // equal parameters to be equal
78
+ $ qbParts = $ options ['query_builder ' ]
79
+ ? array (
80
+ $ options ['query_builder ' ]->getQuery ()->getSQL (),
81
+ $ options ['query_builder ' ]->getParameters ()->toArray (),
82
+ )
83
+ : null ;
84
+
85
+ $ hash = CachingFactoryDecorator::generateHash (array (
86
+ $ options ['em ' ],
87
+ $ options ['class ' ],
88
+ $ qbParts ,
89
+ $ options ['loader ' ],
90
+ ));
91
+
92
+ if (isset ($ choiceLoaders [$ hash ])) {
93
+ return $ choiceLoaders [$ hash ];
88
94
}
89
95
90
96
if ($ options ['loader ' ]) {
@@ -96,18 +102,14 @@ public function configureOptions(OptionsResolver $resolver)
96
102
$ entityLoader = $ type ->getLoader ($ options ['em ' ], $ queryBuilder , $ options ['class ' ]);
97
103
}
98
104
99
- $ choiceLoader = new DoctrineChoiceLoader (
105
+ $ choiceLoaders [ $ hash ] = new DoctrineChoiceLoader (
100
106
$ choiceListFactory ,
101
107
$ options ['em ' ],
102
108
$ options ['class ' ],
103
109
$ entityLoader
104
110
);
105
111
106
- if (null !== $ hash ) {
107
- $ choiceLoaders [$ hash ] = $ choiceLoader ;
108
- }
109
-
110
- return $ choiceLoader ;
112
+ return $ choiceLoaders [$ hash ];
111
113
}
112
114
};
113
115
@@ -199,6 +201,20 @@ public function configureOptions(OptionsResolver $resolver)
199
201
return $ entitiesById ;
200
202
};
201
203
204
+ // Invoke the query builder closure so that we can cache choice lists
205
+ // for equal query builders
206
+ $ queryBuilderNormalizer = function (Options $ options , $ queryBuilder ) {
207
+ if (is_callable ($ queryBuilder )) {
208
+ $ queryBuilder = call_user_func ($ queryBuilder , $ options ['em ' ]->getRepository ($ options ['class ' ]));
209
+
210
+ if (!$ queryBuilder instanceof QueryBuilder) {
211
+ throw new UnexpectedTypeException ($ queryBuilder , 'Doctrine\ORM\QueryBuilder ' );
212
+ }
213
+ }
214
+
215
+ return $ queryBuilder ;
216
+ };
217
+
202
218
$ resolver ->setDefaults (array (
203
219
'em ' => null ,
204
220
'property ' => null , // deprecated, use "choice_label"
@@ -216,9 +232,11 @@ public function configureOptions(OptionsResolver $resolver)
216
232
217
233
$ resolver ->setNormalizer ('em ' , $ emNormalizer );
218
234
$ resolver ->setNormalizer ('choices ' , $ choicesNormalizer );
235
+ $ resolver ->setNormalizer ('query_builder ' , $ queryBuilderNormalizer );
219
236
220
237
$ resolver ->setAllowedTypes ('em ' , array ('null ' , 'string ' , 'Doctrine\Common\Persistence\ObjectManager ' ));
221
238
$ resolver ->setAllowedTypes ('loader ' , array ('null ' , 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface ' ));
239
+ $ resolver ->setAllowedTypes ('query_builder ' , array ('null ' , 'callable ' , 'Doctrine\ORM\QueryBuilder ' ));
222
240
}
223
241
224
242
/**
0 commit comments