Skip to content

SearchFilter: Refactor code to reduce code duplication and makes it easier to add new strategies to classes inheriting from SearchFilter class #3541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Doctrine: Support for foreign identifiers (#4042)
* Doctrine: Support for binary UUID in search filter (#3774)
* Doctrine: Do not add join or lookup for search filter with empty value (#3703)
* Doctrine: Reduce code duplication in search filter (#3541)
* JSON Schema: Allow generating documentation when property and method start from "is" (property `isActive` and method `isActive`) (#4064)
* OpenAPI: Fix missing 422 responses in the documentation (#4086)
* OpenAPI: Fix error when schema is empty (#4051)
Expand Down
56 changes: 31 additions & 25 deletions src/Bridge/Doctrine/MongoDbOdm/Filter/SearchFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,17 @@ protected function filterProperty(string $property, $value, Builder $aggregation
[$matchField, $field, $associations] = $this->addLookupsForNestedProperty($property, $aggregationBuilder, $resourceClass);
}

/**
* @var MongoDBClassMetadata
*/
$metadata = $this->getNestedMetadata($resourceClass, $associations);

$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;
}

/** @var MongoDBClassMetadata */
$metadata = $this->getNestedMetadata($resourceClass, $associations);

if ($metadata->hasField($field) && !$metadata->hasAssociation($field)) {
if ('id' === $field) {
Expand All @@ -107,23 +112,9 @@ protected function filterProperty(string $property, $value, Builder $aggregation
return;
}

$strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;
$this->addEqualityMatchStrategy($strategy, $aggregationBuilder, $field, $matchField, $values, $caseSensitive, $metadata);

// 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);
return;
}

// metadata doesn't have the field, nor an association on the field
Expand All @@ -132,7 +123,6 @@ protected function filterProperty(string $property, $value, Builder $aggregation
}

$values = array_map([$this, 'getIdFromValue'], $values);
$associationFieldIdentifier = 'id';
$doctrineTypeField = $this->getDoctrineFieldType($property, $resourceClass);

if (null !== $this->identifiersExtractor) {
Expand All @@ -149,23 +139,39 @@ protected function filterProperty(string $property, $value, Builder $aggregation
return;
}

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

/**
* Add equality match stage according to the strategy.
*/
private function addEqualityMatchStrategy(string $strategy, Builder $aggregationBuilder, string $field, string $matchField, $values, bool $caseSensitive, ClassMetadata $metadata): void
{
$inValues = [];
foreach ($values as $inValue) {
$inValues[] = $this->getEqualityMatchStrategyValue($strategy, $field, $inValue, $caseSensitive, $metadata);
}

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

/**
* Add equality match stage according to the strategy.
* Get equality match value according to the strategy.
*
* @throws InvalidArgumentException If strategy does not exist
*
* @return Regex|string
*/
private function addEqualityMatchStrategy(string $strategy, string $field, $value, bool $caseSensitive, ClassMetadata $metadata)
private function getEqualityMatchStrategyValue(string $strategy, string $field, $value, bool $caseSensitive, ClassMetadata $metadata)
{
$type = $metadata->getTypeOfField($field);

if (!MongoDbType::hasType($type)) {
return $value;
}
if (MongoDbType::STRING !== $type) {
return MongoDbType::getType($type)->convertToDatabaseValue($value);
}
Expand Down
42 changes: 10 additions & 32 deletions src/Bridge/Doctrine/Orm/Filter/SearchFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
[$alias, $field, $associations] = $this->addJoinsForNestedProperty($property, $alias, $queryBuilder, $queryNameGenerator, $resourceClass);
}

$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;
}

$metadata = $this->getNestedMetadata($resourceClass, $associations);

if ($metadata->hasField($field)) {
Expand All @@ -106,15 +115,6 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
return;
}

$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;
}

$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $values, $caseSensitive, $metadata);

return;
Expand Down Expand Up @@ -145,34 +145,12 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB

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

$type = $metadata->getTypeOfField($associationField);

if (1 === \count($values)) {
$queryBuilder
->andWhere($queryBuilder->expr()->eq($associationAlias.'.'.$associationField, ':'.$valueParameter))
->setParameter($valueParameter, $values[0], $type);

return;
}

$parameters = $queryBuilder->getParameters();
$inQuery = [];

foreach ($values as $val) {
$inQuery[] = ':'.$valueParameter;
$parameters->add(new Parameter($valueParameter, $val, $type));
$valueParameter = $queryNameGenerator->generateParameterName($associationField);
}

$queryBuilder
->andWhere($associationAlias.'.'.$associationField.' IN ('.implode(', ', $inQuery).')')
->setParameters($parameters);
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $associationAlias, $associationField, $values, $caseSensitive, $metadata);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,11 @@ public function provideApplyTestData(): array
$filterFactory,
],
'mixed IRI and entity ID values for relations' => [
sprintf('SELECT %s FROM %s %1$s INNER JOIN %1$s.relatedDummies relatedDummies_a1 WHERE %1$s.relatedDummy IN (:relatedDummy_p1, :relatedDummy_p2) AND relatedDummies_a1.id = :relatedDummies_p4', $this->alias, Dummy::class),
sprintf('SELECT %s FROM %s %1$s INNER JOIN %1$s.relatedDummies relatedDummies_a1 WHERE %1$s.relatedDummy IN (:relatedDummy_p1, :relatedDummy_p2) AND relatedDummies_a1.id = :id_p4', $this->alias, Dummy::class),
[
'relatedDummy_p1' => 1,
'relatedDummy_p2' => 2,
'relatedDummies_p4' => 1,
'id_p4' => 1,
],
$filterFactory,
],
Expand Down