Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/symfony/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Symfony Api平台在从持久层检索实体后过滤实体_Symfony_Api Platform.com_Symfony 4.3 - Fatal编程技术网

Symfony Api平台在从持久层检索实体后过滤实体

Symfony Api平台在从持久层检索实体后过滤实体,symfony,api-platform.com,symfony-4.3,Symfony,Api Platform.com,Symfony 4.3,我有一种情况,我需要在应用后从持久层中提取对象,然后对对象数据和另一个查询参数的过滤基进行一些数学运算 用例:获取给定经纬度10km半径范围内的所有位置 可以将其转换为api端点,如下所示: 我拥有以下实体的位置: * @ApiFilter(SearchFilter::class, properties={ * "longitude": "start", * "latitude":"start", * "city":"partial", * "

我有一种情况,我需要在应用后从持久层中提取对象,然后对对象数据和另一个查询参数的过滤基进行一些数学运算

用例:获取给定经纬度10km半径范围内的所有位置

可以将其转换为api端点,如下所示:

我拥有以下实体的位置:

 * @ApiFilter(SearchFilter::class, properties={
 *      "longitude": "start",
 *      "latitude":"start",
 *      "city":"partial",
 *      "postal_code":"partial",
 *      "address":"partial",
 *    }
 * )
 class Location
 {
  ... 

   public function withinDistance($latitude, $longitude, $distance):?bool
   {
      $location_distance=$this->distanceFrom($latitude,$longitude);
      return $location_distance<=$distance;
   }

 }
为什么对返回的实体对象集合应用这样的筛选器,正确的做法是什么?我不这么认为
ORM过滤器在这种情况下会很有用

您可以在SQL中应用该条件,例如在实体存储库中

class YourEntityRepository {

    public function findByLongLatDist(float lat, float long, float dist) {
        // create your query builder here and return results
    }

}

还可以使用MySQL查询来检索点。并且这个存储库使用特定的MySQL函数。

我发现,通过编写一个脚本来过滤实体是很容易的,并且在从持久层检索实体之后过滤实体。这可能意味着我必须获取所有记录,然后过滤,这是非常昂贵的

正如所建议的,另一种选择是编写一个自定义过滤器来查找距离,如下所示:

定义距离过滤器:

src/过滤器/距离过滤器

src/config/services.yaml

在位置实体上配置api筛选器:

namespace App\Entity;

use App\Dto\LocationOutput;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiFilter;

/**
 * Location
 * 
 * @ApiResource(
 *      collectionOperations={
 *          "get"={
 *              "path"="/getLocationList", 
 *               "filters"={
 *                      "location.distance_filter",
 *                       "location.search_filter"
 *                }
 *           }
 *      },
 *      itemOperations={"get"},
 *      output=LocationOutput::class
 * )

我不确定如何调用该实体存储库函数。如果我们想用php进行计算呢?不过,我将探讨您的想法,我只是想知道是否有可能在从数据库检索对象后对其进行过滤。谢谢你的礼物。我们可以试试这个
<?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 DistanceFilter extends AbstractContextAwareFilter
{

    const DISTANCE=10.0;
    const LAT='latitude';
    const LON='longitude';

    private $appliedAlready=false;

    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
    {
        // otherwise filter is applied to order and page as well
        if ($this->appliedAlready && !$this->isPropertyEnabled($property, $resourceClass) ) {
            return;
        }

        //make sure latitude and longitude are part of specs    
        if(!($this->isPropertyMapped(self::LAT, $resourceClass) && $this->isPropertyMapped(self::LON, $resourceClass)) ){
            return ;
        }

        $query=$this->requestStack->getCurrentRequest()->query;

        $values=[];
        foreach($this->properties as $prop=>$val){
            $this->properties[$prop]=$query->get($prop,null);
        }

        //distance is optional 
        if($this->properties[self::LAT]!=null && $this->properties[self::LON]!=null){
            if($this->properties['distance']==null)
                $this->properties['distance']=self::DISTANCE;
        }else{
            //may be we should raise exception 
            return;
        }

        $this->appliedAlready=True;

        // Generate a unique parameter name to avoid collisions with other filters
        $latParam = $queryNameGenerator->generateParameterName(self::LAT);
        $lonParam = $queryNameGenerator->generateParameterName(self::LON);
        $distParam = $queryNameGenerator->generateParameterName('distance');


        $locationWithinXKmDistance="(
            6371.0 * acos (
                cos ( radians(:$latParam) )
                * cos( radians(o.latitude) )
                * cos( radians(o.longitude) - radians(:$lonParam) )
                + sin ( radians(:$latParam) )
                * sin( radians(o.latitude) )
           )
        )<=:$distParam";

        $queryBuilder
            ->andWhere($locationWithinXKmDistance)
            ->setParameter($latParam, $this->properties[self::LAT])
            ->setParameter($lonParam, $this->properties[self::LON])
            ->setParameter($distParam, $this->properties['distance']);
    }

    // This function is only used to hook in documentation generators (supported by Swagger and Hydra)
    public function getDescription(string $resourceClass): array
    {
        if (!$this->properties) {
            return [];
        }

        $description = [];
        foreach ($this->properties as $property => $strategy) {
            $description["distance_$property"] = [
                'property' => $property,
                'type' => 'string',
                'required' => false,
                'swagger' => [
                    'description' => 'Find locations within given radius',
                    'name' => 'distance_filter',
                    'type' => 'filter',
                ],
            ];
        }

        return $description;
    }
}
doctrine:
     orm:
         dql:
              numeric_functions:
                 acos: DoctrineExtensions\Query\Mysql\Acos
                 cos: DoctrineExtensions\Query\Mysql\Cos
                 sin: DoctrineExtensions\Query\Mysql\Sin
                 radians: DoctrineExtensions\Query\Mysql\Radians
services:
    ....
    App\Filter\DistanceFilter:
      arguments: [ '@doctrine', '@request_stack', '@?logger', {latitude: ~, longitude: ~, distance: ~} ]
      tags:
          - { name: 'api_platform.filter', id: 'location.distance_filter' }
      autowire: false
      autoconfigure: false

    app.location.search_filter:
        parent:        'api_platform.doctrine.orm.search_filter'
        arguments:     [ {"city":"partial","postal_code":"partial","address":"partial"}]
        tags:          [ { name: 'api_platform.filter', id: 'location.search_filter' } ]
        autowire:  false
        autoconfigure: false
namespace App\Entity;

use App\Dto\LocationOutput;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiFilter;

/**
 * Location
 * 
 * @ApiResource(
 *      collectionOperations={
 *          "get"={
 *              "path"="/getLocationList", 
 *               "filters"={
 *                      "location.distance_filter",
 *                       "location.search_filter"
 *                }
 *           }
 *      },
 *      itemOperations={"get"},
 *      output=LocationOutput::class
 * )