Pagination

Pagination screencast
Watch the Pagination screencast

ページネーションのスクリーンキャストを見る

API Platform has native support for paged collections. Pagination is enabled by default for all collections. Each collection contains 30 items per page. The activation of the pagination and the number of elements per page can be configured from:

API プラットフォームは、ページ コレクションをネイティブにサポートしています。ページネーションは、すべてのコレクションでデフォルトで有効になっています。各コレクションには、1 ページあたり 30 のアイテムが含まれています。

  • the server-side (globally or per resource)
    サーバー側 (グローバルまたはリソースごと)
  • the client-side, via a custom GET parameter (disabled by default)
    クライアント側、カスタム GET パラメータ経由 (デフォルトでは無効)

When issuing a GET request on a collection containing more than 1 page (here /books), a Hydra collection is returned. It's a valid JSON(-LD) document containing items of the requested page and metadata.

複数のページを含むコレクション (ここでは /books) に対して GET 要求を発行すると、Hydra コレクションが返されます。これは、要求されたページのアイテムとメタデータを含む有効な JSON(-LD) ドキュメントです。

{
  "@context": "/contexts/Book",
  "@id": "/books",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/books/1",
      "@type": "https://schema.org/Book",
      "name": "My awesome book"
    },
    {
        "_": "Other items in the collection..."
    },
  ],
  "hydra:totalItems": 50,
  "hydra:view": {
    "@id": "/books?page=1",
    "@type": "hydra:PartialCollectionView",
    "hydra:first": "/books?page=1",
    "hydra:last": "/books?page=2",
    "hydra:next": "/books?page=2"
  }
}

Hypermedia links to the first, the last, previous and the next page in the collection are displayed as well as the number of total items in the collection.

コレクション内の最初、最後、前、次のページへのハイパーメディア リンクと、コレクション内の合計アイテム数が表示されます。

The name of the page parameter can be changed with the following configuration:

ページ パラメータの名前は、次の構成で変更できます。

# api/config/packages/api_platform.yaml
api_platform:
    collection:
        pagination:
            page_parameter_name: _page

Disabling the Pagination

Paginating collections is generally accepted as a good practice. It allows browsing large collections without too much overhead as well as preventing DOS attacks. However, for small collections, it can be convenient to fully disable the pagination.

コレクションのページネーションは、一般的に良い習慣として受け入れられています。 DOS 攻撃を防ぐだけでなく、オーバーヘッドをあまりかけずに大きなコレクションを閲覧できます。ただし、小さなコレクションの場合は、ページネーションを完全に無効にすると便利です。

Disabling the Pagination Globally

The pagination can be disabled for all resources using this configuration:

次の構成を使用して、すべてのリソースのページネーションを無効にすることができます。

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        pagination_enabled: false

Disabling the Pagination For a Specific Resource

It can also be disabled for a specific resource:

特定のリソースに対して無効にすることもできます。

[codeSelector]

[コードセレクター]

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationEnabled: false)]
class Book
{
    // ...
}
# api/config/api_platform/resources.yaml
App\Entity\Book:
   paginationEnabled: false

[/codeSelector]

[/コードセレクター]

Disabling the Pagination Client-side

Disabling the Pagination Client-side Globally

You can configure API Platform to let the client enable or disable the pagination. To activate this feature globally, use the following configuration:

クライアントがページネーションを有効または無効にできるように、API プラットフォームを構成できます。この機能をグローバルに有効にするには、次の構成を使用します。

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        pagination_client_enabled: true
    collection:
        pagination:
            enabled_parameter_name: pagination # optional

The pagination can now be enabled or disabled by adding a query parameter named pagination:

pagination という名前のクエリ パラメータを追加することで、ページネーションを有効または無効にできるようになりました。

  • GET /books?pagination=false: disabled
    GET /books?pagination=false: 無効
  • GET /books?pagination=true: enabled
    GET /books?pagination=true: 有効

Any value accepted by the FILTER_VALIDATE_BOOLEAN filter can be used as the value.

FILTER_VALIDATE_BOOLEAN フィルターで受け入れられる任意の値を値として使用できます。

Disabling the Pagination Client-side For a Specific Resource

The client ability to disable the pagination can also be set in the resource configuration:

ページネーションを無効にするクライアント機能は、リソース構成でも設定できます。

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationClientEnabled: true)]
class Book
{
    // ...
}

Changing the Number of Items per Page

In the same manner, the number of items per page is configurable and can be set client-side.

同様に、ページあたりのアイテム数は構成可能であり、クライアント側で設定できます。

Changing the Number of Items per Page Globally

The number of items per page can be configured for all resources:

ページあたりのアイテム数は、すべてのリソースに対して構成できます。

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        pagination_items_per_page: 30 # Default value

Changing the Number of Items per Page For a Specific Resource

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationItemsPerPage: 30)]
class Book
{
    // ...
}

Changing the Number of Items per Page Client-side

Changing the Number of Items per Page Client-side Globally

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        pagination_client_items_per_page: true
    collection:
        pagination:
            items_per_page_parameter_name: itemsPerPage # Default value

The number of items per page can now be changed adding a query parameter named itemsPerPage: GET /books?itemsPerPage=20.

1 ページあたりのアイテム数は、itemsPerPage という名前のクエリ パラメータを追加して変更できるようになりました: GET /books?itemsPerPage=20。

Changing the Number of Items per Page Client-side For a Specific Resource

Changing the number of items per page can be enabled (or disabled) for a specific resource:

ページごとのアイテム数の変更は、特定のリソースに対して有効 (または無効) にすることができます。

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationClientItemsPerPage: true)]
class Book
{
    // ...
}

Changing Maximum Items Per Page

Changing Maximum Items Per Page Globally

The number of maximum items per page can be configured for all resources:

ページあたりの最大アイテム数は、すべてのリソースに対して構成できます。

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        pagination_maximum_items_per_page: 50

Changing Maximum Items Per Page For a Specific Resource

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationMaximumItemsPerPage: 50)]
class Book
{
    // ...
}

Changing Maximum Items Per Page For a Specific Resource Collection Operation

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;

#[ApiResource]
#[GetCollection(paginationMaximumItemsPerPage: 50)]
class Book
{
    // ...
}

Partial Pagination

When using the default pagination, a COUNT query will be issued against the current requested collection. This may have a performance impact on really big collections. The downside is that the information about the last page is lost (ie: hydra:last).

デフォルトのページネーションを使用すると、現在要求されているコレクションに対して COUNT クエリが発行されます。これは、非常に大きなコレクションのパフォーマンスに影響を与える可能性があります。欠点は、最後のページに関する情報が失われることです (例: hydra:last)。

Partial Pagination Globally

The partial pagination retrieval can be configured for all resources:

部分的なページネーションの取得は、すべてのリソースに対して構成できます。

# api/config/packages/api_platform.yaml

api_platform:
    defaults:
        pagination_partial: true # Disabled by default

Partial Pagination For a Specific Resource

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationPartial: true)]
class Book
{
    // ...
}

Partial Pagination Client-side

Partial Pagination Client-side Globally

# api/config/packages/api_platform.yaml

api_platform:
    defaults:
        pagination_client_partial: true # Disabled by default
    collection:
        pagination:
            partial_parameter_name: 'partial' # Default value

The partial pagination retrieval can now be changed by toggling a query parameter named partial: GET /books?partial=true.

部分的なページネーションの取得は、partial という名前のクエリ パラメータを切り替えることで変更できるようになりました: GET /books?partial=true.

Partial Pagination Client-side For a Specific Resource

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(paginationClientPartial: true)]
class Book
{
    // ...
}

Cursor-Based Pagination

To configure your resource to use the cursor-based pagination, select your unique sorted field as well as the direction you’ll like the pagination to go via filters and enable the paginationViaCursor option. Note that for now you have to declare a RangeFilter and an OrderFilter on the property used for the cursor-based pagination.

カーソルベースのページネーションを使用するようにリソースを構成するには、独自の並べ替えフィールドと、フィルターを介してページネーションを行う方向を選択し、paginationViaCursor オプションを有効にします。カーソルベースのページネーションに使用されるプロパティの OrderFilter。

The following configuration also works on a specific operation:

次の構成も特定の操作で機能します。

<?php
// api/src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Odm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Odm\Filter\RangeFilter;

#[ApiResource(
    paginationPartial: true, 
    paginationViaCursor: [
        ['field' => 'id', 'direction' => 'DESC']
    ]
)]
#[ApiFilter(RangeFilter::class, properties: ["id"])]
#[ApiFilter(OrderFilter::class, properties: ["id" => "DESC"])]
class Book
{
    // ...
}

To know more about cursor-based pagination take a look at this blog post on medium (draft).

カーソルベースのページネーションの詳細については、このブログ投稿 (ドラフト) を参照してください。

Controlling The Behavior of The Doctrine ORM Paginator

The PaginationExtension of API Platform performs some checks on the QueryBuilder to guess, in most common cases, the correct values to use when configuring the Doctrine ORM Paginator:

API Platform の PaginationExtension は QueryBuilder でいくつかのチェックを実行し、ほとんどの場合、Doctrine ORM Paginator を構成するときに使用する正しい値を推測します。

  • $fetchJoinCollection argument: Whether there is a join to a collection-valued association. When set to true, the Doctrine ORM Paginator will perform an additional query, in order to get the correct number of results.

    $fetchJoinCollection 引数: コレクション値の関連付けへの結合があるかどうか。 true に設定すると、Doctrine ORM Paginator は正しい数の結果を取得するために追加のクエリを実行します。

    You can configure this using the paginationFetchJoinCollection attribute on a resource or on a per-operation basis:

    これは、リソースまたは操作ごとに paginationFetchJoinCollection 属性を使用して構成できます。

    ```php <?php // api/src/Entity/Book.php namespace App\Entity;

    php

    use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection;

    ApiPlatform\Metadata\ApiResource を使用します。ApiPlatform\Metadata\GetCollection を使用します。

    [ApiResource(paginationFetchJoinCollection: false)]

    [GetCollection]

    [GetCollection(name: 'get_custom', paginationFetchJoinCollection: true)]

    class Book { // ... } ```

    クラスブック{// ...}``

  • setUseOutputWalkers setter: Whether to use output walkers. When set to true, the Doctrine ORM Paginator will use output walkers, which are compulsory for some types of queries.

    setUseOutputWalkers セッター: 出力ウォーカーを使用するかどうか。 true に設定すると、Doctrine ORM Paginator は出力ウォーカーを使用します。これは、一部のタイプのクエリでは必須です。

    You can configure this using the paginationUseOutputWalkers attribute on a resource or on a per-operation basis:

    これは、リソースまたは操作ごとに paginationUseOutputWalkers 属性を使用して構成できます。

    ```php <?php // api/src/Entity/Book.php namespace App\Entity;

    php

    use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection;

    ApiPlatform\Metadata\ApiResource を使用します。ApiPlatform\Metadata\GetCollection を使用します。

    [ApiResource(paginationUseOutputWalkers: false)]

    [GetCollection]

    [GetCollection(name: 'get_custom', paginationUseOutputWalkers: true)]

    class Book { // ... } ```

    クラスブック{// ...}``

For more information, please see the Pagination entry in the Doctrine ORM documentation.

詳細については、Doctrine ORM ドキュメントのページネーション エントリを参照してください。

Custom Controller Action

In case you're using a custom controller action, make sure you return the Paginator object to get the full hydra response with hydra:view (which contains information about first, last, next and previous page). The following examples show how to handle it within a repository method. The controller needs to pass through the page number. You will need to use the Doctrine Paginator and pass it to the API Platform Paginator.

カスタム コントローラー アクションを使用している場合は、必ず Paginator オブジェクトを返して、hydra:view (最初、最後、次、前のページに関する情報を含む) で完全な hydra 応答を取得してください。次の例は、リポジトリ メソッド内でそれを処理する方法を示しています。コントローラーはページ番号を渡す必要があります。 Doctrine Paginator を使用して API Platform Paginator に渡す必要があります。

First example:

最初の例:

<?php
// api/src/Repository/BookRepository.php
namespace App\Repository;

use App\Entity\Book;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
use ApiPlatform\Doctrine\Orm\Paginator;
use Doctrine\Common\Collections\Criteria;

class BookRepository extends ServiceEntityRepository
{
    const ITEMS_PER_PAGE = 20;

    private $tokenStorage;

    public function __construct(
        ManagerRegistry $registry,
        TokenStorageInterface $tokenStorage
    ) {
        parent::__construct($registry, Book::class);

        $this->tokenStorage = $tokenStorage;
    }

    public function getBooksByFavoriteAuthor(int $page = 1): Paginator
    {
        $firstResult = ($page -1) * self::ITEMS_PER_PAGE;

        $user = $this->tokenStorage->getToken()->getUser();
        $queryBuilder = $this->createQueryBuilder();
        $queryBuilder->select('b')
            ->from(Book::class, 'b')
            ->where('b.author = :author')
            ->setParameter('author', $user->getFavoriteAuthor()->getId())
            ->andWhere('b.publicatedOn IS NOT NULL');

        $criteria = Criteria::create()
            ->setFirstResult($firstResult)
            ->setMaxResults(self::ITEMS_PER_PAGE);
        $queryBuilder->addCriteria($criteria);

        $doctrinePaginator = new DoctrinePaginator($queryBuilder);
        $paginator = new Paginator($doctrinePaginator);

        return $paginator;
    }
}

The Controller would look like this:

コントローラーは次のようになります。

<?php
// api/src/Controller/Book/GetBooksByFavoriteAuthorAction.php
namespace App\Controller\Book;

use ApiPlatform\Doctrine\Orm\Paginator;
use App\Repository\BookRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpFoundation\Request;

#[AsController]
class GetBooksByFavoriteAuthorAction extends AbstractController
{
    public function __invoke(Request $request, BookRepository $bookRepository): Paginator
    {
        $page = (int) $request->query->get('page', 1);

        return $bookRepository->getBooksByFavoriteAuthor($page);
    }
}

The service needs to use the proper repository method. You can also use the Query object inside the repository method and pass it to the Paginator instead of passing the QueryBuilder and using Criteria. Second Example:

サービスは適切なリポジトリ メソッドを使用する必要があります。QueryBuilder を渡して Criteria を使用する代わりに、リポジトリ メソッド内で Query オブジェクトを使用して Paginator に渡すこともできます。 2 番目の例:

<?php
// api/src/Repository/BookRepository.php

namespace App\Repository;

// use...

class BookRepository extends ServiceEntityRepository
{
    // constant, variables and constructor...

    public function getBooksByFavoriteAuthor(int $page = 1): Paginator
    {
        $firstResult = ($page -1) * self::ITEMS_PER_PAGE;

        $user = $this->tokenStorage->getToken()->getUser();
        $queryBuilder = $this->createQueryBuilder();
        $queryBuilder->select('b')
            ->from(Book::class, 'b')
            ->where('b.author = :author')
            ->setParameter('author', $user->getFavoriteAuthor()->getId())
            ->andWhere('b.publicatedOn IS NOT NULL');

        $query = $queryBuilder->getQuery()
            ->setFirstResult($firstResult)
            ->setMaxResults(self::ITEMS_PER_PAGE);

        $doctrinePaginator = new DoctrinePaginator($query);
        $paginator = new Paginator($doctrinePaginator);

        return $paginator;
    }
}

Pagination for Custom State Providers

If you are using custom state providers (not the provided Doctrine ORM, ODM or ElasticSearch ones) and if you want your results to be paginated, you will need to return an instance of a ApiPlatform\State\Pagination\PartialPaginatorInterface or ApiPlatform\State\Pagination\PaginatorInterface. A few existing classes are provided to make it easier to paginate the results:

カスタム状態プロバイダー (提供されている Doctrine ORM、ODM、または ElasticSearch のものではない) を使用していて、結果をページ付けしたい場合は、aApiPlatform\State\Pagination\PartialPaginatorInterface または ApiPlatform\State\Pagination\ のインスタンスを返す必要があります。 PaginatorInterface.結果のページ付けを容易にするために、いくつかの既存のクラスが提供されています。

  • ApiPlatform\State\Pagination\ArrayPaginator
    ApiPlatform\State\Pagination\ArrayPaginator
  • ApiPlatform\State\Pagination\TraversablePaginator
    ApiPlatform\State\Pagination\TraversablePaginator