API 平台:使用群组字段的搜索过滤器
API Platform : search filter with group's fields
我正在使用 API 平台,我正在寻找一些东西。
我做了一个这样的个人实体:
class Person
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="string", length=255)
*/
private $firstname;
/**
* @ORM\Column(type="datetime")
*/
private $birthdate;
}
为字段定义组:
App\Entity\Person:
attributes:
id:
groups: ['private']
name:
groups: ['public']
firstname:
groups: ['public']
birthdate:
groups: ['public']
我还明确指出,如果我想要该资源的所有集合,则只应序列化 public 个字段:
App\Entity\Person:
collectionOperations:
get:
filters: ['search_filter']
normalization_context:
groups: ['public']
formats: ['json']
如您所见,我应用了搜索过滤器。在这种情况下,我可以从精确为查询参数的字段中检索资源。
但是,我只想将此过滤器应用于public 字段。
所以 我不想 http://localhost/api/people?id=1 请求有效,因为 id 字段是 private.
我发现可以精确确定作为 SearchFilter 参数的字段,但 精确组名 会更有用,因为我打算使用更多的团体。
我试图查看 GroupFilters,但它对我没有帮助,因为它是一个序列化程序过滤器...
你有什么推荐给我的?
经过几个小时的挖掘,我终于找到了答案:
- 我创建了自己的过滤器,它注入了一个 SearchFilter 实例。
为了比较发送到 QueryParam 的字段组,我不得不使用 AbstractContextAwareFilter class.
扩展我的过滤器
我将这些组与 ClassMetadataFactory class 提供的资源/实体元数据信息进行比较。 为了编写我的组而不是 yaml,我不得不使用注释语法,否则它们将不会被检测到。
如果一个组不在规范化组中,我抛出异常,否则我让 SearchFilter 执行过滤过程。
这是我的作品:
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\QueryBuilder;
use http\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
/**
* Class RestrictGroupFilter
* @package App\Filters
*/
class RestrictGroupFilter extends AbstractContextAwareFilter
{
/**
* @var $decorated AbstractFilter
*/
private $filter;
private $metadataFactory;
public function __construct(AbstractFilter $filter, ClassMetadataFactory $metadataFactory,ManagerRegistry $managerRegistry, ?RequestStack $requestStack = null, LoggerInterface $logger = null, array $properties = null)
{
parent::__construct($managerRegistry, $requestStack, $logger, $properties);
$this->filter = $filter;
$this->metadataFactory = $metadataFactory;
}
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = [])
{
/**
* @var $classMetadata ClassMetadata
*/
$classMetadata = $this->metadataFactory->getMetadataFor($resourceClass); #retrieve of Entity's class's attribute metadata
#prepare to check context's group with normalization ones
foreach ($context["groups"] as $group)
{
if(!in_array($group,$classMetadata->attributesMetadata[$property]->getGroups())){ //if one group is not found in normalization groups
throw new RuntimeException("$property's group denied." /*Groups authorized : ".implode(", ",$context["groups"])*/);
}
}
//Filter is enabled if all is good
$this->filter->filterProperty($property,$value,$queryBuilder,$queryNameGenerator,$resourceClass,$operationName);
}
public function getDescription(string $resourceClass): array
{
// TODO: Implement getDescription() method.
return $this->filter->getDescription($resourceClass);
}
}
对于服务:
search_filter:
parent: 'api_platform.doctrine.orm.search_filter'
tags: ['api_platform.filter']
autowire: false
autoconfigure: false
'App\Filters\RestrictGroupFilter':
arguments: [ '@search_filter','@serializer.mapping.class_metadata_factory']
我正在使用 API 平台,我正在寻找一些东西。 我做了一个这样的个人实体:
class Person
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="string", length=255)
*/
private $firstname;
/**
* @ORM\Column(type="datetime")
*/
private $birthdate;
}
为字段定义组:
App\Entity\Person:
attributes:
id:
groups: ['private']
name:
groups: ['public']
firstname:
groups: ['public']
birthdate:
groups: ['public']
我还明确指出,如果我想要该资源的所有集合,则只应序列化 public 个字段:
App\Entity\Person:
collectionOperations:
get:
filters: ['search_filter']
normalization_context:
groups: ['public']
formats: ['json']
如您所见,我应用了搜索过滤器。在这种情况下,我可以从精确为查询参数的字段中检索资源。
但是,我只想将此过滤器应用于public 字段。 所以 我不想 http://localhost/api/people?id=1 请求有效,因为 id 字段是 private.
我发现可以精确确定作为 SearchFilter 参数的字段,但 精确组名 会更有用,因为我打算使用更多的团体。
我试图查看 GroupFilters,但它对我没有帮助,因为它是一个序列化程序过滤器...
你有什么推荐给我的?
经过几个小时的挖掘,我终于找到了答案:
- 我创建了自己的过滤器,它注入了一个 SearchFilter 实例。
为了比较发送到 QueryParam 的字段组,我不得不使用 AbstractContextAwareFilter class.
扩展我的过滤器
我将这些组与 ClassMetadataFactory class 提供的资源/实体元数据信息进行比较。 为了编写我的组而不是 yaml,我不得不使用注释语法,否则它们将不会被检测到。
如果一个组不在规范化组中,我抛出异常,否则我让 SearchFilter 执行过滤过程。
这是我的作品:
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\QueryBuilder;
use http\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
/**
* Class RestrictGroupFilter
* @package App\Filters
*/
class RestrictGroupFilter extends AbstractContextAwareFilter
{
/**
* @var $decorated AbstractFilter
*/
private $filter;
private $metadataFactory;
public function __construct(AbstractFilter $filter, ClassMetadataFactory $metadataFactory,ManagerRegistry $managerRegistry, ?RequestStack $requestStack = null, LoggerInterface $logger = null, array $properties = null)
{
parent::__construct($managerRegistry, $requestStack, $logger, $properties);
$this->filter = $filter;
$this->metadataFactory = $metadataFactory;
}
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = [])
{
/**
* @var $classMetadata ClassMetadata
*/
$classMetadata = $this->metadataFactory->getMetadataFor($resourceClass); #retrieve of Entity's class's attribute metadata
#prepare to check context's group with normalization ones
foreach ($context["groups"] as $group)
{
if(!in_array($group,$classMetadata->attributesMetadata[$property]->getGroups())){ //if one group is not found in normalization groups
throw new RuntimeException("$property's group denied." /*Groups authorized : ".implode(", ",$context["groups"])*/);
}
}
//Filter is enabled if all is good
$this->filter->filterProperty($property,$value,$queryBuilder,$queryNameGenerator,$resourceClass,$operationName);
}
public function getDescription(string $resourceClass): array
{
// TODO: Implement getDescription() method.
return $this->filter->getDescription($resourceClass);
}
}
对于服务:
search_filter:
parent: 'api_platform.doctrine.orm.search_filter'
tags: ['api_platform.filter']
autowire: false
autoconfigure: false
'App\Filters\RestrictGroupFilter':
arguments: [ '@search_filter','@serializer.mapping.class_metadata_factory']