Skip to content

Commit

Permalink
Merge c407e20 into cfc05d1
Browse files Browse the repository at this point in the history
  • Loading branch information
jpierront committed May 2, 2020
2 parents cfc05d1 + c407e20 commit cd44ca4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 72 deletions.
49 changes: 27 additions & 22 deletions src/Bridge/Doctrine/MongoDbOdm/Filter/SearchFilter.php
Expand Up @@ -19,7 +19,6 @@
use ApiPlatform\Core\Bridge\Doctrine\Common\Filter\SearchFilterTrait;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Aggregation\Builder;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as MongoDBClassMetadata;
use Doctrine\ODM\MongoDB\Types\Type as MongoDbType;
Expand Down Expand Up @@ -94,6 +93,14 @@ protected function filterProperty(string $property, $value, Builder $aggregation

$caseSensitive = true;

$strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;

// prefixing the strategy with i makes it case insensitive
if (0 === strpos($strategy, 'i')) {
$strategy = substr($strategy, 1);
$caseSensitive = false;
}

if ($metadata->hasField($field) && !$metadata->hasAssociation($field)) {
if ('id' === $field) {
$values = array_map([$this, 'getIdFromValue'], $values);
Expand All @@ -107,23 +114,8 @@ protected function filterProperty(string $property, $value, Builder $aggregation
return;
}

$strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;

// prefixing the strategy with i makes it case insensitive
if (0 === strpos($strategy, 'i')) {
$strategy = substr($strategy, 1);
$caseSensitive = false;
}

$inValues = [];
foreach ($values as $inValue) {
$inValues[] = $this->addEqualityMatchStrategy($strategy, $field, $inValue, $caseSensitive, $metadata);
}

$aggregationBuilder
->match()
->field($matchField)
->in($inValues);
$type = $metadata->getTypeOfField($field);
$this->addEqualityMatchStrategy($strategy, $aggregationBuilder, $matchField, $type, $values, $caseSensitive);
}

// metadata doesn't have the field, nor an association on the field
Expand All @@ -149,10 +141,25 @@ protected function filterProperty(string $property, $value, Builder $aggregation
return;
}

$this->addEqualityMatchStrategy($strategy, $aggregationBuilder, $matchField, $doctrineTypeField, $values, $caseSensitive);
}

/**
* Add equality match stage according to the strategy.
*
* @throws InvalidArgumentException If strategy does not exist
*/
private function addEqualityMatchStrategy(string $strategy, Builder $aggregationBuilder, string $matchField, $fieldType, $values, bool $caseSensitive)
{
$inValues = [];
foreach ($values as $inValue) {
$inValues[] = $this->getEqualityMatchStrategy($strategy, $inValue, $caseSensitive, $fieldType);
}

$aggregationBuilder
->match()
->field($matchField)
->in($values);
->in($inValues);
}

/**
Expand All @@ -162,10 +169,8 @@ protected function filterProperty(string $property, $value, Builder $aggregation
*
* @return Regex|string
*/
private function addEqualityMatchStrategy(string $strategy, string $field, $value, bool $caseSensitive, ClassMetadata $metadata)
private function getEqualityMatchValue(string $strategy, $value, bool $caseSensitive, string $type)
{
$type = $metadata->getTypeOfField($field);

switch ($strategy) {
case MongoDbType::STRING !== $type:
return MongoDbType::getType($type)->convertToDatabaseValue($value);
Expand Down
87 changes: 37 additions & 50 deletions src/Bridge/Doctrine/Orm/Filter/SearchFilter.php
Expand Up @@ -95,6 +95,14 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
$doctrineTypeField = $this->getDoctrineFieldType($property, $resourceClass);
$values = array_map([$this, 'getIdFromValue'], $values);

$strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;

// prefixing the strategy with i makes it case insensitive
if (0 === strpos($strategy, 'i')) {
$strategy = substr($strategy, 1);
$caseSensitive = false;
}

if ($metadata->hasField($field)) {
if (!$this->hasValidValues($values, $doctrineTypeField)) {
$this->logger->notice('Invalid filter ignored', [
Expand All @@ -104,34 +112,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
return;
}

$strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;

// prefixing the strategy with i makes it case insensitive
if (0 === strpos($strategy, 'i')) {
$strategy = substr($strategy, 1);
$caseSensitive = false;
}

if (1 === \count($values)) {
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $doctrineTypeField, $values[0], $caseSensitive);

return;
}

if (self::STRATEGY_EXACT !== $strategy) {
$this->logger->notice('Invalid filter ignored', [
'exception' => new InvalidArgumentException(sprintf('"%s" strategy selected for "%s" property, but only "%s" strategy supports multiple values', $strategy, $property, self::STRATEGY_EXACT)),
]);

return;
}

$wrapCase = $this->createWrapCase($caseSensitive);
$valueParameter = $queryNameGenerator->generateParameterName($field);

$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' IN (:%s)', $alias, $field, $valueParameter))
->setParameter($valueParameter, $caseSensitive ? $values : array_map('strtolower', $values));
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $doctrineTypeField, $values, $caseSensitive);
}

// metadata doesn't have the field, nor an association on the field
Expand All @@ -155,63 +136,69 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
return;
}

$association = $field;
$valueParameter = $queryNameGenerator->generateParameterName($association);
if ($metadata->isCollectionValuedAssociation($association)) {
$associationAlias = QueryBuilderHelper::addJoinOnce($queryBuilder, $queryNameGenerator, $alias, $association);
$valueParameter = $queryNameGenerator->generateParameterName($field);
if ($metadata->isCollectionValuedAssociation($field)) {
$associationAlias = QueryBuilderHelper::addJoinOnce($queryBuilder, $queryNameGenerator, $alias, $field);
$associationField = $associationFieldIdentifier;
} else {
$associationAlias = $alias;
$associationField = $field;
}

if (1 === \count($values)) {
$queryBuilder
->andWhere(sprintf('%s.%s = :%s', $associationAlias, $associationField, $valueParameter))
->setParameter($valueParameter, $values[0], $doctrineTypeField);
} else {
$queryBuilder
->andWhere(sprintf('%s.%s IN (:%s)', $associationAlias, $associationField, $valueParameter))
->setParameter($valueParameter, $values, $doctrineTypeField);
}
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $associationAlias, $associationField, $doctrineTypeField, $values, $caseSensitive, $valueParameter);
}

/**
* Adds where clause according to the strategy.
*
* @throws InvalidArgumentException If strategy does not exist
*/
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $fieldType, $value, bool $caseSensitive)
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $fieldType, $values, bool $caseSensitive, string $valueParameter = null)
{
$wrapCase = $this->createWrapCase($caseSensitive);
$valueParameter = $queryNameGenerator->generateParameterName($field);
$valueParameter = $valueParameter ?? $queryNameGenerator->generateParameterName($field);
$values = (array) $values;

if (self::STRATEGY_EXACT !== $strategy && 1 !== \count($values)) {
$this->logger->notice('Invalid filter ignored', [
'exception' => new InvalidArgumentException(sprintf('"%s" strategy selected for "%s" property, but only "%s" strategy supports multiple values', $strategy, $field, self::STRATEGY_EXACT)),
]);

return;
}

switch ($strategy) {
case null:
case self::STRATEGY_EXACT:
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' = '.$wrapCase(':%s'), $alias, $field, $valueParameter))
->setParameter($valueParameter, $value, $fieldType);
if (1 === \count($values)) {
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' = '.$wrapCase(':%s'), $alias, $field, $valueParameter))
->setParameter($valueParameter, $values[0], $fieldType);
} else {
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' IN (:%s)', $alias, $field, $valueParameter))
->setParameter($valueParameter, $caseSensitive ? $values : array_map('strtolower', $values));
}
break;
case self::STRATEGY_PARTIAL:
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s, \'%%\')'), $alias, $field, $valueParameter))
->setParameter($valueParameter, $value, $fieldType);
->setParameter($valueParameter, $values[0], $fieldType);
break;
case self::STRATEGY_START:
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(:%s, \'%%\')'), $alias, $field, $valueParameter))
->setParameter($valueParameter, $value, $fieldType);
->setParameter($valueParameter, $values[0], $fieldType);
break;
case self::STRATEGY_END:
$queryBuilder
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s)'), $alias, $field, $valueParameter))
->setParameter($valueParameter, $value, $fieldType);
->setParameter($valueParameter, $values[0], $fieldType);
break;
case self::STRATEGY_WORD_START:
$queryBuilder
->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))
->setParameter($valueParameter, $value, $fieldType);
->setParameter($valueParameter, $values[0], $fieldType);
break;
default:
throw new InvalidArgumentException(sprintf('strategy %s does not exist.', $strategy));
Expand Down

0 comments on commit cd44ca4

Please sign in to comment.