如何使用自定义过滤器清理输出(API 平台)
How to sanitize output with custom filter (API Platform)
我正在尝试使用 api-platform (2.6.5) 在我的实体上添加距离过滤器。一切正常,除了响应主体:
"hydra:member": [
{
"0": {
"@id": "/api/places/1",
"@type": "Place",
"id": 1,
"name": "test",
"description": "test",
"slug": "test",
"address": "test",
"country": "Suisse",
"latitude": "50.6300000",
"longitude": "3.0620000",
"tableCount": 10,
"tablePlace": 6,
"tableCapacity": 60
},
"distance": "8.887448516728334"
},
{
"0": {
"@id": "/api/places/8",
...
},
"distance": "10.273770606986691"
},
{...}
计算的距离不被视为一个实体 属性 和 api 平台 return 它们分开(这是前端的问题)。
这是我的实体:
/**
* @ORM\Entity(repositoryClass=PlaceRepository::class)
* @ApiResource(
* normalizationContext={"groups"={"place", "place:read"}, "swagger_definition_name": "Read"},
* denormalizationContext={"groups"={"place", "place:write"}, "swagger_definition_name": "Write"},
* )
* @ApiFilter(SearchFilter::class, properties={"name": "partial", "description": "partial", "zip":"start", "city": "exact"})
* @ApiFilter(NearbyFilter::class, properties={"location"})
* @Entity @HasLifecycleCallbacks
*/
class Place
{
....
/**
* @ORM\Column(type="decimal", precision=10, scale=7, nullable=true)
* @Groups({"place"})
*/
private $latitude;
/**
* @ORM\Column(type="decimal", precision=10, scale=7, nullable=true)
* @Groups({"place"})
*/
private $longitude;
/**
* @var float The distance calculated
* @Groups({"place:read"})
*/
private $distance;
这是我的过滤器:
final class NearbyFilter extends AbstractContextAwareFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
if ($property !== 'location') {
return;
}
[$lat, $lng, $radius] = explode(',', $value);
// add distance in DQL based on filter location point
$queryBuilder
->addSelect('
(6371.0 * acos (
cos ( radians(:latitude) )
* cos( radians(o.latitude) )
* cos( radians(o.longitude) - radians(:longitude) )
+ sin ( radians(:latitude) )
* sin( radians(o.latitude) )
)) AS distance')
->having('distance <= :radius')
->setParameter('radius', $radius)
->setParameter('latitude', $lat)
->setParameter('longitude', $lng)
->orderBy('distance')
;
}
如您所见,我尝试添加未映射的 属性 'distance' 但没有效果:/
我也尝试使用 Dto 和 Denormalizer 来解决这个问题但没有成功(但我是新手)。
你能解决我的问题吗?
谢谢!
终于找到了 DataProvider 的解决方案:
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Place;
class PlaceDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
private $collectionDataProvider;
public function __construct(CollectionDataProviderInterface $collectionDataProvider)
{
$this->collectionDataProvider = $collectionDataProvider;
}
public function getCollection(string $resourceClass, string $operationName = null, array $context = [])
{
$places = $this->collectionDataProvider->getCollection($resourceClass, $operationName, $context);
foreach ($places as &$place) {
if(!$place instanceof Place && is_array($place)) {
$sanitizedPlace = $place[0];
if( !is_null( $place['distance'] ) ){
$sanitizedPlace = $this->setDistance($sanitizedPlace, $place['distance']);
}
$place = $sanitizedPlace;
}
}
return $places;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return $resourceClass === Place::class;
}
public function setDistance(Place $place, $distance): Place
{
$place->setDistance($distance);
return $place;
}
}
谢谢
我正在尝试使用 api-platform (2.6.5) 在我的实体上添加距离过滤器。一切正常,除了响应主体:
"hydra:member": [
{
"0": {
"@id": "/api/places/1",
"@type": "Place",
"id": 1,
"name": "test",
"description": "test",
"slug": "test",
"address": "test",
"country": "Suisse",
"latitude": "50.6300000",
"longitude": "3.0620000",
"tableCount": 10,
"tablePlace": 6,
"tableCapacity": 60
},
"distance": "8.887448516728334"
},
{
"0": {
"@id": "/api/places/8",
...
},
"distance": "10.273770606986691"
},
{...}
计算的距离不被视为一个实体 属性 和 api 平台 return 它们分开(这是前端的问题)。
这是我的实体:
/**
* @ORM\Entity(repositoryClass=PlaceRepository::class)
* @ApiResource(
* normalizationContext={"groups"={"place", "place:read"}, "swagger_definition_name": "Read"},
* denormalizationContext={"groups"={"place", "place:write"}, "swagger_definition_name": "Write"},
* )
* @ApiFilter(SearchFilter::class, properties={"name": "partial", "description": "partial", "zip":"start", "city": "exact"})
* @ApiFilter(NearbyFilter::class, properties={"location"})
* @Entity @HasLifecycleCallbacks
*/
class Place
{
....
/**
* @ORM\Column(type="decimal", precision=10, scale=7, nullable=true)
* @Groups({"place"})
*/
private $latitude;
/**
* @ORM\Column(type="decimal", precision=10, scale=7, nullable=true)
* @Groups({"place"})
*/
private $longitude;
/**
* @var float The distance calculated
* @Groups({"place:read"})
*/
private $distance;
这是我的过滤器:
final class NearbyFilter extends AbstractContextAwareFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
if ($property !== 'location') {
return;
}
[$lat, $lng, $radius] = explode(',', $value);
// add distance in DQL based on filter location point
$queryBuilder
->addSelect('
(6371.0 * acos (
cos ( radians(:latitude) )
* cos( radians(o.latitude) )
* cos( radians(o.longitude) - radians(:longitude) )
+ sin ( radians(:latitude) )
* sin( radians(o.latitude) )
)) AS distance')
->having('distance <= :radius')
->setParameter('radius', $radius)
->setParameter('latitude', $lat)
->setParameter('longitude', $lng)
->orderBy('distance')
;
}
如您所见,我尝试添加未映射的 属性 'distance' 但没有效果:/ 我也尝试使用 Dto 和 Denormalizer 来解决这个问题但没有成功(但我是新手)。
你能解决我的问题吗?
谢谢!
终于找到了 DataProvider 的解决方案:
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Place;
class PlaceDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
private $collectionDataProvider;
public function __construct(CollectionDataProviderInterface $collectionDataProvider)
{
$this->collectionDataProvider = $collectionDataProvider;
}
public function getCollection(string $resourceClass, string $operationName = null, array $context = [])
{
$places = $this->collectionDataProvider->getCollection($resourceClass, $operationName, $context);
foreach ($places as &$place) {
if(!$place instanceof Place && is_array($place)) {
$sanitizedPlace = $place[0];
if( !is_null( $place['distance'] ) ){
$sanitizedPlace = $this->setDistance($sanitizedPlace, $place['distance']);
}
$place = $sanitizedPlace;
}
}
return $places;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return $resourceClass === Place::class;
}
public function setDistance(Place $place, $distance): Place
{
$place->setDistance($distance);
return $place;
}
}
谢谢