Collection Data Provider를 생성하고 Doctrine Extension, Filters 및 Pagination을 유지하는 방법 [Api Platform]

API 플랫폼에서 컬렉션의 쿼리를 재정의하고 페이지 매김, 필터 및 교리 확장을 유지하는 방법을 여러분과 공유하고 싶습니다.

전체 프로젝트를 만들지는 않겠지만 내 저장소로 이동하여 고정물이 있는 프로젝트에서 공급자를 볼 수 있습니다.

이것은 저장소https://github.com/aratinau/api-platform-pagination의 홈페이지입니다(API 플랫폼을 사용하여 사용자 정의 데이터로 페이지 매김을 생성하기 위해 여러 다른 예제를 보여줍니다).
여기에서 병합 요청을 볼 수 있습니다: https://github.com/aratinau/api-platform-pagination/commit/1d27f16adecda1c0956fcc5d0da81f017d915c1b

매개변수includeArchived가 누락된 경우 보관되지 않은 책만 반환하는 제공자를 만들 것입니다. 매개변수가 includeArchived=true인 경우 모든 책(아카이브 여부)

그런 다음 정의된 로케일로 책만 반환하는 Doctrine Extension을 만듭니다.

이것은 우리의 책 엔티티src/Entity/Book.php입니다.

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\BookRepository;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;

/**
 * @ApiResource()
 * @ApiFilter(OrderFilter::class)
 * @ORM\Entity(repositoryClass=BookRepository::class)
 */
class Book
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $title;

    /**
     * @ORM\Column(type="boolean")
     */
    private $isArchived;

    /**
     * @ORM\Column(type="string", length=4)
     */
    private $locale;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getIsArchived(): ?bool
    {
        return $this->isArchived;
    }

    public function setIsArchived(bool $isArchived): self
    {
        $this->isArchived = $isArchived;

        return $this;
    }

    public function getLocale(): ?string
    {
        return $this->locale;
    }

    public function setLocale(string $locale): self
    {
        $this->locale = $locale;

        return $this;
    }
}


만들기src/DataProvider/BookCollectionDataProvider.php
<?php

namespace App\DataProvider;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryResultCollectionExtensionInterface;
use App\DTO\SessionParameter;
use App\Entity\Book;
use App\Entity\Session;
use Doctrine\Persistence\ManagerRegistry;

class BookCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
    public function __construct(
        private ManagerRegistry $managerRegistry,
        private $collectionExtensions,
    ) {
    }

    public function getCollection(
        string $resourceClass,
        string $operationName = null,
        array $context = []
    ): iterable {
        // here we define to false when the param 'includeArchived' is missing (by default)
        $includeArchived = $context['filters']['includeArchived'] ?? 'false';

        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
        $repository = $manager->getRepository($resourceClass);
        $queryBuilder = $repository->createQueryBuilder('o');
        $alias = $queryBuilder->getRootAliases()[0];

        // by default we want only books not archived
        if ($includeArchived === 'false') {
            $queryBuilder->andWhere("$alias.isArchived = false");
        }

        /*
            Then we will add to all extensions our queryBuilder updated.
            We could inject only the extension we needs but I wanted to show them all to you.
            The first extension (BookExtension) will be created after
        */
        $queryNameGenerator = new QueryNameGenerator();
        foreach ($this->collectionExtensions as $extension) {
            /*
             * Extensions are (in this order)
             * - "App\Doctrine\BookExtension"
             * - "ApiPlatform\Doctrine\Orm\Extension\FilterExtension"
             * - "ApiPlatform\Doctrine\Orm\Extension\FilterEagerLoadingExtension"
             * - "ApiPlatform\Doctrine\Orm\Extension\EagerLoadingExtension"
             * - "ApiPlatform\Doctrine\Orm\Extension\OrderExtension"
             * - "ApiPlatform\Doctrine\Orm\Extension\PaginationExtension"
             */
            $extension->applyToCollection(
                $queryBuilder,
                $queryNameGenerator,
                $resourceClass,
                $operationName,
                $context
            );

            /*
                This next condition check if we have the pagination activated (by default is yes) and the result is returned
            */
            if (
                $extension instanceof QueryResultCollectionExtensionInterface
                &&
                $extension->supportsResult($resourceClass, $operationName, $context)
            ) {
                return $extension->getResult($queryBuilder, $resourceClass, $operationName, $context);
            }
        }

        // we are here only if we have deactivate the pagination
        return $queryBuilder->getQuery()->getResult();
    }

    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return Book::class === $resourceClass;
    }
}

$collectionExtensions를 삽입하려면 config/services.yaml를 추가해야 합니다.

App\DataProvider\BookCollectionDataProvider:
    bind:
        $collectionExtensions: !tagged api_platform.doctrine.orm.query_extension.collection


이제 파일을 생성합니다src/Doctrine/BookExtension.php.

<?php

namespace App\Doctrine;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use App\Entity\Book;
use Doctrine\ORM\QueryBuilder;

class BookExtension implements QueryCollectionExtensionInterface
{
    private const LOCALE = 'fr';

    public function applyToCollection(
        QueryBuilder $queryBuilder,
        $queryNameGenerator,
        string $resourceClass,
        string $operationName = null
    ) {
        /*
            We ask to return only the book with the locale 'fr'
        */
        if ($resourceClass === Book::class) {
            $rootAlias = $queryBuilder->getRootAliases()[0];
            $queryBuilder
                ->andWhere("$rootAlias.locale = :locale")
                ->setParameter('locale', self::LOCALE)
            ;
        }
    }
}


이제 Book 엔터티 필터(예: OrderFilter)를 추가하고 컬렉션 공급자를 통해 필터링된 컬렉션을 유지할 수 있습니다.

GET/books 보관되지 않은 책을 반환합니다.
GET/books?includeArchived=false 보관되지 않은 책을 반환합니다.
GET/books?includeArchived=true은 모든 책을 반환합니다.

당신이 좋아 바랍니다! 🙂🚀

좋은 웹페이지 즐겨찾기