21
21
use ApiPlatform \Core \Bridge \Doctrine \Orm \Util \QueryNameGeneratorInterface ;
22
22
use ApiPlatform \Core \Exception \InvalidArgumentException ;
23
23
use Doctrine \DBAL \Types \Type as DBALType ;
24
+ use Doctrine \ORM \Query \Parameter ;
24
25
use Doctrine \ORM \QueryBuilder ;
25
26
use Doctrine \Persistence \ManagerRegistry ;
27
+ use Doctrine \Persistence \Mapping \ClassMetadata ;
26
28
use Psr \Log \LoggerInterface ;
27
29
use Symfony \Component \HttpFoundation \RequestStack ;
28
30
use Symfony \Component \PropertyAccess \PropertyAccess ;
@@ -113,7 +115,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
113
115
$ caseSensitive = false ;
114
116
}
115
117
116
- $ this ->addWhereByStrategy ($ strategy , $ queryBuilder , $ queryNameGenerator , $ alias , $ field , $ values , $ caseSensitive );
118
+ $ this ->addWhereByStrategy ($ strategy , $ queryBuilder , $ queryNameGenerator , $ alias , $ field , $ values , $ caseSensitive, $ metadata );
117
119
118
120
return ;
119
121
}
@@ -149,14 +151,39 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
149
151
$ associationField = $ associationFieldIdentifier ;
150
152
}
151
153
152
- if (1 === \count ($ values )) {
154
+ /*
155
+ * If field type is string/float, Doctrine does not call convertToDatabaseValueSQL() because it
156
+ * does not know it needs conversion.
157
+ * This would lead to incorrect values for Ramsey\Uuid\Doctrine\UuidBinaryType for example.
158
+ * The only fix is to provide field type to doctrine ...
159
+ * It's easy if setParameter() sets only one value BUT impossible if multiple values are provided.
160
+ * The only way to do this is to rewrite the IN() statement to multiple values
161
+ * and map a global setParameters()
162
+ */
163
+ $ type = $ metadata ->getTypeOfField ($ associationField );
164
+ $ nbValues = \count ($ values );
165
+
166
+ if (1 === $ nbValues ) {
153
167
$ queryBuilder
154
168
->andWhere ($ queryBuilder ->expr ()->eq ($ associationAlias .'. ' .$ associationField , ': ' .$ valueParameter ))
155
- ->setParameter ($ valueParameter , $ values [0 ]);
169
+ ->setParameter ($ valueParameter , $ values [0 ], $ type );
156
170
} else {
171
+ // get current parameters, because QueryBuilder->setParameters() erase previous parameters set
172
+ $ parameters = $ queryBuilder ->getParameters ();
173
+ $ inQuery = [];
174
+
175
+ // convertToDatabaseValue() can only convert one value at a time ... We can no longer pass an array of values, we should use multiple values
176
+
177
+ for ($ i = 0 ; $ i < $ nbValues ; ++$ i ) {
178
+ $ inQuery [] = ': ' .$ valueParameter ;
179
+ $ parameters ->add (new Parameter ($ valueParameter , $ values [$ i ], $ type ));
180
+ $ valueParameter = $ queryNameGenerator ->generateParameterName ($ associationField );
181
+ }
182
+
183
+ // we cannot use expr->in() here because it considers $inQuery parameters as strings.
157
184
$ queryBuilder
158
- ->andWhere ($ queryBuilder -> expr ()-> in ( $ associationAlias .'. ' .$ associationField, ' : ' . $ valueParameter ) )
159
- ->setParameter ( $ valueParameter , $ values );
185
+ ->andWhere ($ associationAlias .'. ' .$ associationField. ' IN ( ' . implode ( ' , ' , $ inQuery ). ' ) ' )
186
+ ->setParameters ( $ parameters );
160
187
}
161
188
}
162
189
@@ -165,28 +192,47 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
165
192
*
166
193
* @throws InvalidArgumentException If strategy does not exist
167
194
*/
168
- protected function addWhereByStrategy (string $ strategy , QueryBuilder $ queryBuilder , QueryNameGeneratorInterface $ queryNameGenerator , string $ alias , string $ field , $ values , bool $ caseSensitive )
195
+ protected function addWhereByStrategy (string $ strategy , QueryBuilder $ queryBuilder , QueryNameGeneratorInterface $ queryNameGenerator , string $ alias , string $ field , $ values , bool $ caseSensitive/*, ClassMetadata $metadata = null*/ )
169
196
{
197
+ // check if we have metadata
198
+ if (\func_num_args () > 7 && ($ metadata = func_get_arg (7 )) instanceof ClassMetadata) {
199
+ $ type = $ metadata ->getTypeOfField ($ field );
200
+ } else {
201
+ @trigger_error (sprintf ('Method %s() will have a 8th argument `$metadata` in version API Platform 3.0. ' , __FUNCTION__ ), E_USER_DEPRECATED );
202
+ $ type = null ; // default setParameter() value
203
+ }
204
+
170
205
if (!\is_array ($ values )) {
171
206
$ values = [$ values ];
172
207
}
173
208
209
+ $ nbValues = \count ($ values );
174
210
$ wrapCase = $ this ->createWrapCase ($ caseSensitive );
175
211
$ valueParameter = ': ' .$ queryNameGenerator ->generateParameterName ($ field );
176
212
$ aliasedField = sprintf ('%s.%s ' , $ alias , $ field );
177
213
178
214
if (null == $ strategy || self ::STRATEGY_EXACT == $ strategy ) {
179
- if (1 == \count ( $ values ) ) {
215
+ if (1 == $ nbValues ) {
180
216
$ queryBuilder
181
217
->andWhere ($ queryBuilder ->expr ()->eq ($ wrapCase ($ aliasedField ), $ wrapCase ($ valueParameter )))
182
- ->setParameter ($ valueParameter , $ values [0 ]);
218
+ ->setParameter ($ valueParameter , $ values [0 ], $ type );
183
219
184
220
return ;
185
221
}
186
222
223
+ // get current parameters, because QueryBuilder->setParameters() erase previous parameters set
224
+ $ parameters = $ queryBuilder ->getParameters ();
225
+ $ inQuery = [];
226
+ for ($ i = 0 ; $ i < $ nbValues ; ++$ i ) {
227
+ $ inQuery [] = $ valueParameter ;
228
+ $ parameters ->add (new Parameter ($ valueParameter , $ caseSensitive ? $ values [$ i ] : strtolower ($ values [$ i ]), $ type ));
229
+ $ valueParameter = ': ' .$ queryNameGenerator ->generateParameterName ($ field );
230
+ }
231
+
232
+ // we cannot use expr->in() here because it considers $inQuery parameters as strings.
187
233
$ queryBuilder
188
- ->andWhere ($ queryBuilder -> expr ()-> in ( $ wrapCase ($ aliasedField ), $ valueParameter ) )
189
- ->setParameter ( $ valueParameter , $ caseSensitive ? $ values : array_map ( ' strtolower ' , $ values ) );
234
+ ->andWhere ($ wrapCase ($ aliasedField ). ' IN ( ' . implode ( ' , ' , $ inQuery ). ' ) ' )
235
+ ->setParameters ( $ parameters );
190
236
191
237
return ;
192
238
}
@@ -228,7 +274,7 @@ protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuild
228
274
}
229
275
230
276
$ queryBuilder ->andWhere ($ queryBuilder ->expr ()->orX (...$ ors ));
231
- array_walk ($ parameters , [$ queryBuilder , 'setParameter ' ]);
277
+ array_walk ($ parameters , [$ queryBuilder , 'setParameter ' ], $ type );
232
278
}
233
279
234
280
/**
0 commit comments