| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- <?php
- declare(strict_types=1);
- namespace App\Filter\Utils;
- use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
- use Doctrine\ORM\QueryBuilder;
- use Dunglas\ApiBundle\Api\ResourceInterface;
- use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
- use JetBrains\PhpStorm\ArrayShape;
- /**
- * Add a distance filter to en entity
- *
- * To use it, add the following query :
- *
- * withinDistance=({latitude}, {longitude}, {distance})
- *
- * Where latitude and longitude are the coordinates of the origine point, and distance the maximum
- * distance in Km.
- *
- * /!\ The subject entity shall have a longitude and a latitude properties
- */
- final class DistanceFilter extends AbstractFilter
- {
- /**
- * API docs
- * @param string|ResourceInterface $resource
- * @return array[]
- */
- #[ArrayShape(['search' => "array"])]
- public function getDescription(string|ResourceInterface $resource): array
- {
- if (!property_exists($resource, 'latitude') || !property_exists($resource, 'longitude')) {
- throw new \RuntimeException('DistanceFilter can only used with resources having both latitude and longitude properties');
- }
- return [
- 'search' => [
- 'property' => 'withinDistance',
- 'type' => 'string',
- 'required' => false,
- 'swagger' => [
- 'description' => "Filtre une entity selon sa distance (km) à un point (latitude, longitude). " .
- "L'entité doit-elle aussi posséder des propriétés 'latitude' et 'longitude'." .
- "Pass the following query to use it : `withinDistance=({latitude}, {longitude}, {distance})`, " .
- "where {latitude} and {longitude} are the coordinates of the origine point, and {distance} the maximum distance in Km.",
- 'name' => 'Distance Filter',
- 'type' => 'Utils Filter',
- ],
- ]
- ];
- }
- protected function filterProperty(
- string $property,
- $value,
- QueryBuilder $queryBuilder,
- QueryNameGeneratorInterface
- $queryNameGenerator, string
- $resourceClass,
- string $operationName = null
- ): void
- {
- if ($property !== 'withinDistance') {
- return;
- }
- if (!preg_match('/^(\d+(\.\d+)?,){2}\d+(\.\d+)?$/', $value)) {
- throw new \RuntimeException('DistanceFilter : Invalid argument, please pass latitude, longitude and distance to the parameter as comma separated floating numbers.');
- }
- [$latitude, $longitude, $distance] = explode(',', $value);
- $alias = $queryBuilder->getRootAliases()[0];
- // Generate unique parameters names to avoid collisions with other filters
- $latitudeParameterName = $queryNameGenerator->generateParameterName('latitude');
- $longitudeParameterName = $queryNameGenerator->generateParameterName('longitude');
- $distanceParameterName = $queryNameGenerator->generateParameterName('distance');
- $queryBuilder
- ->andWhere(
- sprintf(
- 'SPHERICAL_DISTANCE(%1$s.latitude, %1$s.longitude, :%2$s, :%3$s) <= :%4$s',
- $alias, $latitudeParameterName, $longitudeParameterName, $distanceParameterName
- )
- )
- ->setParameter($latitudeParameterName, $latitude)
- ->setParameter($longitudeParameterName, $longitude)
- ->setParameter($distanceParameterName, $distance);
- }
- }
|