Passed
Pull Request — 2.2 (#1808)
by Antoine
03:11
created

PaginationTrait.php$0 ➔ getPaginator()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Util;
15
16
use ApiPlatform\Core\Bridge\Doctrine\Orm\AbstractPaginator;
17
use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
18
use Doctrine\ORM\QueryBuilder;
19
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrineOrmPaginator;
20
21
/**
22
 * @author Antoine Bluchet <[email protected]>
23
 */
24
trait PaginationTrait
25
{
26
    private $managerRegistry;
27
28
    /**
29
     * Determines whether the Paginator should fetch join collections, if the root entity uses composite identifiers it should not.
30
     *
31
     * @see https://github.com/doctrine/doctrine2/issues/2910
32
     *
33
     * @param QueryBuilder $queryBuilder
34
     *
35
     * @return bool
36
     */
37
    private function useFetchJoinCollection(QueryBuilder $queryBuilder): bool
38
    {
39
        return !QueryChecker::hasRootEntityWithCompositeIdentifier($queryBuilder, $this->managerRegistry);
40
    }
41
42
    /**
43
     * Determines whether output walkers should be used.
44
     *
45
     * @param QueryBuilder $queryBuilder
46
     *
47
     * @return bool
48
     */
49
    private function useOutputWalkers(QueryBuilder $queryBuilder): bool
50
    {
51
        /*
52
         * "Cannot count query that uses a HAVING clause. Use the output walkers for pagination"
53
         *
54
         * @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php#L50
55
         */
56
        if (QueryChecker::hasHavingClause($queryBuilder)) {
57
            return true;
58
        }
59
60
        /*
61
         * "Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."
62
         *
63
         * @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L87
64
         */
65
        if (QueryChecker::hasRootEntityWithForeignKeyIdentifier($queryBuilder, $this->managerRegistry)) {
66
            return true;
67
        }
68
69
        /*
70
         * "Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers."
71
         *
72
         * @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L149
73
         */
74
        if (
75
            QueryChecker::hasMaxResults($queryBuilder) &&
76
            QueryChecker::hasOrderByOnToManyJoin($queryBuilder, $this->managerRegistry)
77
        ) {
78
            return true;
79
        }
80
81
        /*
82
         * When using composite identifiers pagination will need Output walkers
83
         */
84
        if (QueryChecker::hasRootEntityWithCompositeIdentifier($queryBuilder, $this->managerRegistry)) {
85
            return true;
86
        }
87
88
        // Disable output walkers by default (performance)
89
        return false;
90
    }
91
92
    /**
93
     * Creates a new Doctrine\ORM\Tools\Pagination\Paginator from a given QueryBuilder.
94
     */
95
    private function getDoctrinePaginator(QueryBuilder $queryBuilder): DoctrineOrmPaginator
96
    {
97
        $doctrineOrmPaginator = new DoctrineOrmPaginator($queryBuilder, $this->useFetchJoinCollection($queryBuilder));
98
        $doctrineOrmPaginator->setUseOutputWalkers($this->useOutputWalkers($queryBuilder));
99
100
        return $doctrineOrmPaginator;
101
    }
102
103
    /**
104
     * Use this to get an instance of ApiPlatform\Core\Bridge\Doctrine\Orm\AbstractPaginator when the pagination is partial.
105
     */
106
    private function getPartialPaginator(DoctrineOrmPaginator $paginator)
107
    {
108
        return new class($paginator) extends AbstractPaginator {
109
        };
110
    }
111
112
    /**
113
     * Creates a new ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator.
114
     */
115
    private function getPaginator(DoctrineOrmPaginator $paginator)
116
    {
117
        return new Paginator($paginator);
118
    }
119
}
120