Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,61 @@ awareness about deprecated code.

# Upgrade to 4.0

## Removed `Doctrine\ORM\Tools\Pagination\Paginator`

The class deprecated in 3.7 has been removed. Use
`Doctrine\ORM\Tools\Pagination\OffsetPaginator` instead: the first result and the
maximum number of results are no longer read implicitly from the query, they are
passed together as a `Window` value object to `paginate()`, which returns an
immutable, iterable `WindowPage`.

```diff
-use Doctrine\ORM\Tools\Pagination\Paginator;
+use Doctrine\ORM\Tools\Pagination\OffsetPaginator;
+use Doctrine\ORM\Tools\Pagination\Window;

-$query = $entityManager->createQuery($dql)
- ->setFirstResult(0)
- ->setMaxResults(25);
+$query = $entityManager->createQuery($dql);

-$paginator = new Paginator($query, fetchJoinCollection: true);
+$page = (new OffsetPaginator(fetchJoinCollection: true))
+ ->paginate($query, new Window(0, 25));

-$total = count($paginator);
+$total = $page->getTotalCount();

-foreach ($paginator as $post) {
+foreach ($page as $post) {
// ...
}
```

`Paginator::setUseOutputWalkers()` becomes the `useOutputWalkers` constructor
argument of `OffsetPaginator`.

A `Window` always carries a page size, so paginating without a limit is no longer
possible: run the query itself when you want every row.

`WindowPage::getItems()` returns a list, whereas iterating the legacy `Paginator`
preserved the result keys of a DQL `INDEX BY` clause.

## Removed `Paginator::HINT_ENABLE_DISTINCT`

The constant deprecated in 3.7 has been removed along with its class. Use
`Doctrine\ORM\Tools\Pagination\PaginatorInterface::HINT_ENABLE_DISTINCT` instead, which
applies to both `OffsetPaginator` and `CursorPaginator`. Its value is unchanged,
so hints set as a raw string keep working.

```diff
-use Doctrine\ORM\Tools\Pagination\Paginator;
+use Doctrine\ORM\Tools\Pagination\PaginatorInterface;

-$query->setHint(Paginator::HINT_ENABLE_DISTINCT, false);
+$query->setHint(PaginatorInterface::HINT_ENABLE_DISTINCT, false);
```

## Forbid using the `WITH` keyword for arbitrary DQL joins

Using the `WITH` keyword to specify the condition for an arbitrary DQL join is
Expand Down
29 changes: 0 additions & 29 deletions docs/en/tutorials/pagination.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,35 +168,6 @@ API Reference
``Window::getPageNumber(): int``
Returns the 1-based page number the window points at.

Legacy ``Paginator``
~~~~~~~~~~~~~~~~~~~~~

.. deprecated:: 3.7

The ``Paginator`` class is deprecated in favor of ``OffsetPaginator`` and
will be removed in 4.0.

The legacy ``Paginator`` reads the offset implicitly from the query
(``setFirstResult()`` / ``setMaxResults()``) and implements the SPL interfaces
``Countable`` and ``IteratorAggregate``:

.. code-block:: php

<?php
use Doctrine\ORM\Tools\Pagination\Paginator;

$dql = "SELECT p, c FROM BlogPost p JOIN p.comments c";
$query = $entityManager->createQuery($dql)
->setFirstResult(0)
->setMaxResults(100);

$paginator = new Paginator($query, fetchJoinCollection: true);

$c = count($paginator);
foreach ($paginator as $post) {
echo $post->getHeadline() . "\n";
}

Cursor-Based Pagination
-----------------------

Expand Down
1 change: 0 additions & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@

<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming">
<exclude-pattern>src/EntityManagerInterface.php</exclude-pattern>
<!-- the Paginator name is taken by the deprecated class until its removal in 4.0 -->
<exclude-pattern>src/Tools/Pagination/PaginatorInterface.php</exclude-pattern>
</rule>

Expand Down
6 changes: 0 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2682,12 +2682,6 @@ parameters:
count: 1
path: src/Tools/Pagination/LimitSubqueryOutputWalker.php

-
message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\Paginator\:\:count\(\) should return int\<0, max\> but returns int\.$#'
identifier: return.type
count: 1
path: src/Tools/Pagination/Paginator.php

-
message: '#^Method Doctrine\\ORM\\Tools\\ResolveTargetEntityListener\:\:remapAssociation\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
identifier: missingType.generics
Expand Down
9 changes: 0 additions & 9 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ parameters:
checkMissingOverrideMethodAttribute: true

ignoreErrors:
# Paginator is deprecated in favor of OffsetPaginator, but the static-analysis
# fixture still legitimately references it until its removal in 4.0.
-
identifier: return.deprecatedClass
path: tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php
-
identifier: parameter.deprecatedClass
path: tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php

# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'

Expand Down
4 changes: 1 addition & 3 deletions src/Tools/Pagination/OffsetPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
* which returns an immutable, iterable {@see WindowPage} rather than the
* paginator itself. The paginator therefore holds no state beyond its
* configuration: a single instance can be shared as a service and reused for
* any query and any page. This avoids the implicit offset handling and the
* stateful API of the legacy {@see Paginator}, which this class is intended to
* replace.
* any query and any page.
*
* @template-covariant T
* @implements PaginatorInterface<T, Window>
Expand Down
115 changes: 0 additions & 115 deletions src/Tools/Pagination/Paginator.php

This file was deleted.

13 changes: 6 additions & 7 deletions src/Tools/Pagination/PaginatorQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
use function is_string;

/**
* Provides the implementation shared by {@see Paginator}, {@see OffsetPaginator}
* and {@see CursorPaginator}.
* Provides the implementation shared by {@see OffsetPaginator} and
* {@see CursorPaginator}.
*
* Every method takes the query it works on as an argument, so that the
* stateless paginators can serve any query.
Expand All @@ -31,8 +31,7 @@ trait PaginatorQuery
{
/**
* Whether to force the use of an output walker. Null lets the paginator
* decide. The legacy {@see Paginator} exposes a setter for it, the
* stateless paginators take it as a constructor argument.
* decide. Paginators take it as a constructor argument.
*/
private bool|null $useOutputWalkers = null;

Expand Down Expand Up @@ -128,9 +127,9 @@ private function getCountQuery(Query $query): Query
/**
* Executes the query for the given offset window and returns the entities.
*
* Shared by {@see Paginator} and {@see OffsetPaginator}: when a to-many
* collection is fetch-joined, it uses the ID subquery + WHERE IN strategy to
* return the correct number of root entities despite duplicate rows.
* Used by {@see OffsetPaginator}: when a to-many collection is fetch-joined,
* it uses the ID subquery + WHERE IN strategy to return the correct number
* of root entities despite duplicate rows.
*
* The result keys are preserved (e.g. for DQL ``INDEX BY``); callers that
* need a list should apply {@see array_values()} themselves.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Doctrine\StaticAnalysis\Tools\Pagination;

use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\ORM\Tools\Pagination\WindowPage;

/** @template-covariant T of object */
abstract class PaginatorFactory
abstract class PageFactory
{
/** @var class-string<T> */
private $class;
Expand All @@ -24,8 +24,8 @@ public function getClass(): string
return $this->class;
}

/** @phpstan-return Paginator<T> */
abstract public function createPaginator(): Paginator;
/** @phpstan-return WindowPage<T> */
abstract public function createPage(): WindowPage;
}

interface Animal
Expand All @@ -36,18 +36,18 @@ class Cat implements Animal
{
}

/** @param Paginator<Animal> $paginator */
function getFirstAnimal(Paginator $paginator): Animal|null
/** @param WindowPage<Animal> $page */
function getFirstAnimal(WindowPage $page): Animal|null
{
foreach ($paginator as $result) {
foreach ($page as $result) {
return $result;
}

return null;
}

/** @param PaginatorFactory<Cat> $catPaginatorFactory */
function test(PaginatorFactory $catPaginatorFactory): Animal|null
/** @param PageFactory<Cat> $catPageFactory */
function test(PageFactory $catPageFactory): Animal|null
{
return getFirstAnimal($catPaginatorFactory->createPaginator());
return getFirstAnimal($catPageFactory->createPage());
}
Loading
Loading