Last active
August 19, 2021 17:39
-
-
Save renta/b6ece3fec7896440fe52a9ec0e76571a to your computer and use it in GitHub Desktop.
Custom OR filter for an Api Platform
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Filter; | |
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter; | |
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface; | |
use Doctrine\ORM\QueryBuilder; | |
final class OrSearchFilter extends AbstractContextAwareFilter | |
{ | |
private const FILTER_KEY = 'orSearch'; | |
/** | |
* Passes a property through the filter. | |
* | |
* @param string $property | |
* @param $value | |
* @param QueryBuilder $queryBuilder | |
* @param QueryNameGeneratorInterface $queryNameGenerator | |
* @param string $resourceClass | |
* @param string|null $operationName | |
*/ | |
protected function filterProperty( | |
string $property, | |
$value, | |
QueryBuilder $queryBuilder, | |
QueryNameGeneratorInterface $queryNameGenerator, | |
string $resourceClass, | |
string $operationName = null | |
): void { | |
if (null === $value || false === strpos($property, self::FILTER_KEY)) { | |
return; | |
} | |
$parameterName = $queryNameGenerator->generateParameterName($property); | |
$search = []; | |
$mappedJoins = []; | |
foreach ($this->properties as $groupName => $fields) { | |
foreach ($fields as $field) { | |
$joins = explode('.', $field); | |
for ($lastAlias = 'o', $i = 0, $num = \count($joins); $i < $num; $i++) { | |
$currentAlias = $joins[$i]; | |
if ($i === $num - 1) { | |
$search[] = "LOWER({$lastAlias}.{$currentAlias}) LIKE LOWER(:{$parameterName})"; | |
} else { | |
$join = "{$lastAlias}.{$currentAlias}"; | |
if (!\in_array($join, $mappedJoins, true)) { | |
$queryBuilder->leftJoin($join, $currentAlias); | |
$mappedJoins[] = $join; | |
} | |
} | |
$lastAlias = $currentAlias; | |
} | |
} | |
} | |
$queryBuilder->andWhere(implode(' OR ', $search)); | |
$queryBuilder->setParameter($parameterName, '%' . $value . '%'); | |
} | |
/** | |
* Gets the description of this filter for the given resource. | |
* | |
* Returns an array with the filter parameter names as keys and array with the following data as values: | |
* - property: the property where the filter is applied | |
* - type: the type of the filter | |
* - required: if this filter is required | |
* - strategy: the used strategy | |
* - swagger (optional): additional parameters for the path operation, | |
* e.g. 'swagger' => [ | |
* 'description' => 'My Description', | |
* 'name' => 'My Name', | |
* 'type' => 'integer', | |
* ] | |
* The description can contain additional data specific to a filter. | |
* | |
* @see \ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer::getFiltersParameters | |
* | |
* @param string $resourceClass | |
* | |
* @return array | |
*/ | |
public function getDescription(string $resourceClass): array | |
{ | |
$description = []; | |
foreach ($this->properties as $groupName => $fields) { | |
$description[self::FILTER_KEY . '_' . $groupName] = [ | |
'property' => self::FILTER_KEY, | |
'type' => 'string', | |
'required' => false, | |
'swagger' => ['description' => 'OrSearchFilter on ' . implode(', ', $fields)], | |
]; | |
} | |
return $description; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
#... other services | |
App\Filter\OrSearchFilter: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Entity; | |
use App\Filter\OrSearchFilter; | |
use ApiPlatform\Core\Annotation\ApiFilter; | |
//...more imports here | |
/** | |
* @ApiFilter( | |
* OrSearchFilter::class, properties={ | |
* "fullname": {"firstName", "lastName"} | |
* } | |
* ) | |
* @ORM\Entity(repositoryClass="App\Repository\UserRepository") | |
*/ | |
class User implements UserInterface | |
{ | |
//...more fields here | |
/** | |
* @Assert\Length(min="1", max="100") | |
* @ORM\Column(type="string", length=100, nullable=true) | |
*/ | |
private $firstName; | |
/** | |
* @Assert\Length(min="1", max="100") | |
* @ORM\Column(type="string", length=100, nullable=true) | |
*/ | |
private $lastName; |
Thanks @renta !
In case someone need something a little bit different, I made a mix from this gist and another one (https://gist.github.com/masseelch/47931f3a745409f8f44c69efa9ecb05c) :
https://gist.github.com/masacc/94df641b3cb9814cbdaeb3f158d2e1f7
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks for your share !