Требуется фильтр на платформе Api

Я использую платформу API, и я определил настраиваемый фильтр после https://api-platform.com/docs/core/filters/#creating-custom-filters

Он работает нормально, но мне нужно, чтобы этот фильтр требовался каждый раз, когда приложение выполняет HTTP-запрос GET для определенного объекта (где установлен фильтр).

Я проверил этот код:

// 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["similar_$property"] = [
            'property' => $property,
            'type' => 'string',
            'required' => false,
            'swagger' => [
                'description' => 'Filter using a similar postgres function.',
                'name' => $property,
                'type' => 'string',
            ],
        ];
    }

    return $description;
}

Хотя в getDescription есть обязательное поле, оно работает только для документации api, а не для HTTP-запроса.

В настоящее время комментарий класса ApiPlatform \ Core \ Filter \ QueryParameterValidateListener: проверяет параметры запроса в зависимости от описания фильтра. Однако он работает только для операций сбора с именем get. (сервис api_platform.listener.view.validate_query_parameters настроен с class = ApiPlatform \ Core \ Filter \ QueryParameterValidateListener в качестве kernel.event_listener с event = kernel.request)   —  person AythaNzt    schedule 02.12.2020

См. также:  Symfony: ошибка установки пакета администрирования sonata
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. AythaNzt

    Вы можете применить фильтр через систему событий.

    /**
     * @ApiResource
     */
    class Resource {
        ...
    }
    
    <?php
    
    namespace App\EventSubscriber;
    
    use ApiPlatform\Core\EventListener\EventPriorities;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpKernel\Event\ControllerEvent;
    use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
    use Symfony\Component\HttpKernel\KernelEvents;
    
    class ResourceReadSubscriber implements EventSubscriberInterface
    {
        /**
         * @return array The event names to listen to
         */
        public static function getSubscribedEvents()
        {
            return [
                KernelEvents::CONTROLLER => [
                    ['hasFilter', EventPriorities::PRE_READ],
                ],
            ];
        }
    
        public function hasFilter(ControllerEvent $event)
        {
            // first check if this affects the requested resource
            $resource = $event->getRequest()->attributes->get('_api_resource_class');
    
            if (Resource::class !== $resource) {
                return;
            }
    
            // second check if this is the get_collection controller
            $controller = $event->getRequest()->attributes->get('_controller');
    
            if ('api_platform.action.get_collection' !== $controller) {
                return;
            }
    
            // third validate the required filter is set
            // we expect a filter via GET parameter 'filter-query-parameter'
            if (!$event->getRequest()->query->has('filter-query-parameter')) {
                throw new BadRequestHttpException('Filter is required');
            }
        }
    }
    
  2. AythaNzt

    Я применил фильтр с помощью специального поставщика данных.

    Установив для auto wire и autoconfigure значение true, я добавил это в App \ DataProvider:

    <?php
    
    namespace App\DataProvider;
    
    use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
    use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
    use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterExtension;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultCollectionExtensionInterface;
    use Doctrine\Common\Persistence\ManagerRegistry;
    use ApiPlatform\Core\Exception\RuntimeException;
    use App\Entity\Activity;
    
    final class EventRequireCollectionDataProvider implements CollectionDataProviderInterface, RestrictedDataProviderInterface
    {
        private $collectionExtensions;
        private $managerRegistry;
    
        public function __construct(ManagerRegistry $managerRegistry, iterable $collectionExtensions)
        {
          $this->managerRegistry = $managerRegistry;
          $this->collectionExtensions = $collectionExtensions;
        }
    
        public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
        {
            return Activity::class === $resourceClass;
        }
    
        /**
         * {@inheritdoc}
         *
         * @throws RuntimeException
         */
        public function getCollection(string $resourceClass, string $operationName = null, array $context = [])
        {
    
            //This is where the filter is enforced...
            if (empty($context) || !array_key_exists ( "filters" , $context ) || !array_key_exists ( "event" , $context['filters'] ) || !ctype_digit($context['filters']['event'])){
                throw new RuntimeException('The api call must have the event id parameter set.');
            }
            $manager = $this->managerRegistry->getManagerForClass($resourceClass);
            $repository = $manager->getRepository($resourceClass);
            $queryBuilder = $repository->createQueryBuilder('o');
            $queryNameGenerator = new QueryNameGenerator();
    
            //This goes through all the built in extensions Api Platform provides. 
            //The filter is automatically added to the query here
                foreach ($this->collectionExtensions as $extension) {
                    $extension->applyToCollection($queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context);
                    if ($extension instanceof QueryResultCollectionExtensionInterface && $extension->supportsResult($resourceClass, $operationName)){
                        $result = $extension->getResult($queryBuilder, $resourceClass, $operationName, $context);
                        return $result;
                    }
                }
                $queryBuilder->getQuery()->getResult();
            }
        }
    

    Моя сущность (то, что имеет значение — моя сущность также использует нормализатор группы):

    <?php
    
    namespace App\Entity;
    
    use ApiPlatform\Core\Annotation\ApiFilter;
    use ApiPlatform\Core\Annotation\ApiProperty;
    use ApiPlatform\Core\Annotation\ApiResource;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
    use ApiPlatform\Core\Serializer\Filter\PropertyFilter;
    use ApiPlatform\Core\Action\NotFoundAction;
    use Symfony\Component\Serializer\Annotation\Groups;
    use Symfony\Component\Serializer\Annotation\MaxDepth;
    use ApiPlatform\Core\Annotation\ApiSubresource;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\ORM\Mapping as ORM;
    use App\Controller\DoNothingController;
    use Symfony\Component\Validator\Constraints as Assert;
    
    /**
     * @ApiResource(
     *      iri="https://schema.org/Event",
     *      collectionOperations={
     *          "get" = {
     *              "normalization_context"={"groups"={"event_activities"}},
     *              "swagger_context"={
     *                  "summary" = "Retrieves the activities for a specific event.",
     *                  "description" = "This operation will obtain all the activities (i.e., the schedule) for a particular event. Currently, the event parameter is required and only one can be submitted at a time. Otherwise, an error will occur."
     *              },
     *          }
     *      },
     *      itemOperations={
     *          "get" = {
        *          "controller"=NotFoundAction::class,
       *           "read"=false,
        *          "output"=false,
     *          }
     *      },
     * )    
     * @ApiFilter(SearchFilter::class, properties={"event": "exact"})
     * @ORM\Entity(repositoryClass="App\Repository\ActivityRepository")
     */
    class Activity
    {
        /**
         * @var integer The activity identifier 
         * 
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         * @Groups({"event_activities"})
         */
        private $id;
    
         /**
         * @var \DateTime The start time for the activity.
         * @ORM\Column(type="datetimetz", nullable=true)
         * @Groups({"event_activities"})
         */
        private $startTime;
    
        /**
         * @var \DateTime The ending time for the activity.
         * @ORM\Column(type="datetimetz", nullable=true)
         * @Groups({"event_activities"})
         * @ApiProperty(iri="https://schema.org/endDate")
         */
        private $endTime;
    
        /**
         * @var \DateTimeInterval The duration of the activity. 
         * @Groups({"event_activities"})
         * @ORM\Column(type="dateinterval", nullable=true)
         */
        private $duration;
    
        /**
         * @var VenueLoc The venue or location of the activity in accordance to the map for the event.
         * 
         * @ORM\ManyToOne(targetEntity="App\Entity\VenueLoc", inversedBy="activities")
         * @Groups({"event_activities"})
         */
        private $venueLoc;
    
        /**
         * @var Event The event this activity is associated with.
         * 
         * @ORM\ManyToOne(targetEntity="App\Entity\Event", inversedBy="activities")
         */
        private $event;
    
    More properties, getters, setters, etc...
    

    И в сервис.ямл.

    #This is required for the custom Data Provider to use all the ApiPlatform's built in extensions like order, filter, pagination, etc.
    #See EventRequireCollectionDataProvider for an example on how to grab and use all the built in extensions
    #Allows for the custom Data Provider to act like a normal Data Provider 
    #Only works for Doctrine ORM I guess.
    App\DataProvider\EventRequireCollectionDataProvider:
        arguments:
          $collectionExtensions: !tagged api_platform.doctrine.orm.query_extension.collection
    
    #This is required to call the custom swagger decorator to correct the parameters documentation to also require the filter and not allow any other parameters 
    App\Swagger\SwaggerEventRequireDecorator:
        decorates: 'api_platform.swagger.normalizer.api_gateway'
        arguments: [ '@App\Swagger\SwaggerEventRequireDecorator.inner' ]
        autoconfigure: false
    

    И в App \ Swagger исправить документацию Swagger на шлюзе.

    <?php
    
    namespace App\Swagger;
    
    use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
    
    final class SwaggerEventRequireDecorator implements NormalizerInterface
    {
        private $decorated;
    
        public function __construct(NormalizerInterface $decorated)
        {
            $this->decorated = $decorated;
        }
    
        public function normalize($object, $format = null, array $context = [])
        {
            $docs = $this->decorated->normalize($object, $format, $context);
    
            $customDefinition = [
                'name' => 'event',
                'description' => 'ID of the event the activities belong to.',
                'in' => 'query',
                'required' => 'true',
                'type' => 'integer'
            ];
    
    //         e.g. remove an existing event parameter
            $docs['paths']['/scheduleamap-api/activities']['get']['parameters'] = array_values(array_filter($docs['paths']['/scheduleamap-api/activities']['get']['parameters'], function ($param) {
                return $param['name'] !== 'event';
            }));
    
        // e.g. add the new definition for event
        $docs['paths']['/scheduleamap-api/activities']['get']['parameters'][] = $customDefinition;
    
            // Remove other restricted parameters that will generate errors.
            $docs['paths']['/scheduleamap-api/activities']['get']['parameters'] = array_values(array_filter($docs['paths']['/scheduleamap-api/activities']['get']['parameters'], function ($param) {
                return $param['name'] !== 'event[]';
            }));
    
            return $docs;
        }
    
        public function supportsNormalization($data, $format = null)
        {
            return $this->decorated->supportsNormalization($data, $format);
        }
    }
    
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: