Skip to content

Commit bf34b36

Browse files
committed
Handle binary UUID in SearchFilter
Most difficult work was to rewrite IN queries.
1 parent 1c7c26d commit bf34b36

File tree

1 file changed

+40
-10
lines changed

1 file changed

+40
-10
lines changed

src/Bridge/Doctrine/Orm/Filter/SearchFilter.php

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
2222
use ApiPlatform\Core\Exception\InvalidArgumentException;
2323
use Doctrine\DBAL\Types\Type as DBALType;
24+
use Doctrine\ORM\Query\Parameter;
2425
use Doctrine\ORM\QueryBuilder;
2526
use Doctrine\Persistence\ManagerRegistry;
27+
use Doctrine\Persistence\Mapping\ClassMetadata;
2628
use Psr\Log\LoggerInterface;
2729
use Symfony\Component\HttpFoundation\RequestStack;
2830
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -114,7 +116,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
114116
}
115117

116118
if (1 === \count($values)) {
117-
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $values[0], $caseSensitive);
119+
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $values[0], $caseSensitive, $metadata);
118120

119121
return;
120122
}
@@ -128,11 +130,32 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
128130
}
129131

130132
$wrapCase = $this->createWrapCase($caseSensitive);
131-
$valueParameter = $queryNameGenerator->generateParameterName($field);
133+
134+
/*
135+
* If field type is string/float, Doctrine does not call convertToDatabaseValueSQL() because it
136+
* does not know it needs conversion.
137+
* This would lead to incorrect values for Ramsey\Uuid\Doctrine\UuidBinaryType for example.
138+
* The only fix is to provide field type to doctrine ...
139+
* BUT it is not possible with setParameter(), as third arg can only be
140+
* \Doctrine\DBAL\Connection::PARAM_STR_ARRAY
141+
* The only way to do this will be to rewrite the IN() statement to multiple values,
142+
* each provided as a single setParameter()
143+
*/
144+
$inQuery = [];
145+
$args = [];
146+
$type = $metadata->getTypeOfField($field);
147+
$nbValues = \count($values);
148+
149+
for ($i = 0; $i < $nbValues; ++$i) {
150+
$valueParameter = $queryNameGenerator->generateParameterName($field);
151+
$inQuery[] = ':'.$valueParameter;
152+
$args[] = new Parameter($valueParameter, $caseSensitive ? $values[$i] : strtolower($values[$i]), $type);
153+
}
132154

133155
$queryBuilder
134-
->andWhere(sprintf($wrapCase('%s.%s').' IN (:%s)', $alias, $field, $valueParameter))
135-
->setParameter($valueParameter, $caseSensitive ? $values : array_map('strtolower', $values));
156+
->andWhere(sprintf($wrapCase('%s.%s').' IN (%s)', $alias, $field, implode(',', $inQuery)))
157+
->setParameters($args);
158+
136159
}
137160

138161
// metadata doesn't have the field, nor an association on the field
@@ -184,8 +207,15 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
184207
*
185208
* @throws InvalidArgumentException If strategy does not exist
186209
*/
187-
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $value, bool $caseSensitive)
210+
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $value, bool $caseSensitive, ClassMetadata $metadata = null)
188211
{
212+
// check if we have metadata
213+
if ($metadata instanceof ClassMetadata) {
214+
$type = $metadata->getTypeOfField($field);
215+
} else {
216+
@trigger_error('addWhereByStrategy() will require argument ClassMetadata in 3.0.', E_USER_DEPRECATED);
217+
$type = null; // default setParameter() value
218+
}
189219
$wrapCase = $this->createWrapCase($caseSensitive);
190220
$valueParameter = $queryNameGenerator->generateParameterName($field);
191221

@@ -194,27 +224,27 @@ protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuild
194224
case self::STRATEGY_EXACT:
195225
$queryBuilder
196226
->andWhere(sprintf($wrapCase('%s.%s').' = '.$wrapCase(':%s'), $alias, $field, $valueParameter))
197-
->setParameter($valueParameter, $value);
227+
->setParameter($valueParameter, $value, $type);
198228
break;
199229
case self::STRATEGY_PARTIAL:
200230
$queryBuilder
201231
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s, \'%%\')'), $alias, $field, $valueParameter))
202-
->setParameter($valueParameter, $value);
232+
->setParameter($valueParameter, $value, $type);
203233
break;
204234
case self::STRATEGY_START:
205235
$queryBuilder
206236
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(:%s, \'%%\')'), $alias, $field, $valueParameter))
207-
->setParameter($valueParameter, $value);
237+
->setParameter($valueParameter, $value, $type);
208238
break;
209239
case self::STRATEGY_END:
210240
$queryBuilder
211241
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s)'), $alias, $field, $valueParameter))
212-
->setParameter($valueParameter, $value);
242+
->setParameter($valueParameter, $value, $type);
213243
break;
214244
case self::STRATEGY_WORD_START:
215245
$queryBuilder
216246
->andWhere(sprintf($wrapCase('%1$s.%2$s').' LIKE '.$wrapCase('CONCAT(:%3$s, \'%%\')').' OR '.$wrapCase('%1$s.%2$s').' LIKE '.$wrapCase('CONCAT(\'%% \', :%3$s, \'%%\')'), $alias, $field, $valueParameter))
217-
->setParameter($valueParameter, $value);
247+
->setParameter($valueParameter, $value, $type);
218248
break;
219249
default:
220250
throw new InvalidArgumentException(sprintf('strategy %s does not exist.', $strategy));

0 commit comments

Comments
 (0)