Completed
Pull Request — master (#9)
by
unknown
03:07
created

QueryAnalyser::analyseQueryWithoutPager()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 11
cts 11
cp 1
rs 9.6666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
declare(strict_types=1);
3
4
namespace Paysera\Pagination\Service\Doctrine;
5
6
use Doctrine\ORM\Query\Expr\Select;
7
use Doctrine\ORM\QueryBuilder;
8
use Paysera\Pagination\Entity\Doctrine\AnalysedQuery;
9
use Paysera\Pagination\Entity\OrderingConfiguration;
10
use Paysera\Pagination\Entity\Doctrine\ConfiguredQuery;
11
use Paysera\Pagination\Entity\OrderingPair;
12
use Paysera\Pagination\Entity\Pager;
13
use InvalidArgumentException;
14
use Paysera\Pagination\Exception\TooLargeOffsetException;
15
16
class QueryAnalyser
17
{
18
    const ID_FIELD = 'id';
19
20
    /**
21
     * @internal
22
     * @param ConfiguredQuery $configuredQuery
23
     * @param Pager $pager
24
     * @return AnalysedQuery
25
     */
26 42
    public function analyseQuery(ConfiguredQuery $configuredQuery, Pager $pager): AnalysedQuery
27
    {
28 42
        $analysedQuery = $this->analyseQueryWithoutPager($configuredQuery);
29 37
        if ($configuredQuery->getQueryModifier() !== null) {
30 1
            $analysedQuery->setQueryModifier($configuredQuery->getQueryModifier());
31
        }
32
33
        if (
34 37
            $pager->getOffset() !== null
35 37
            && $configuredQuery->hasMaximumOffset()
36 37
            && $pager->getOffset() > $configuredQuery->getMaximumOffset()
37
        ) {
38 1
            throw new TooLargeOffsetException($configuredQuery->getMaximumOffset(), $pager->getOffset());
39
        }
40
41 37
        $orderingConfigurations = $this->mapToOrderingConfigurations(
42 37
            $configuredQuery,
43 37
            $analysedQuery->getRootAlias(),
44 37
            $pager->getOrderingPairs()
45
        );
46
47
        return $analysedQuery
48 36
            ->setOrderingConfigurations($orderingConfigurations)
49
        ;
50
    }
51
52
    /**
53
     * @internal
54
     * @param ConfiguredQuery $configuredQuery
55
     * @return AnalysedQuery
56
     */
57 48
    public function analyseQueryWithoutPager(ConfiguredQuery $configuredQuery): AnalysedQuery
58
    {
59 48
        $queryBuilder = $configuredQuery->getQueryBuilder();
60 48
        $rootAlias = $this->getRootAlias($queryBuilder);
61 48
        if ($rootAlias === null) {
62 5
            throw new InvalidArgumentException('Invalid QueryBuilder passed - cannot resolve root select alias');
63
        }
64
65 43
        $analysedQuery = (new AnalysedQuery())
66 43
            ->setQueryBuilder($queryBuilder)
67 43
            ->setRootAlias($rootAlias)
68
        ;
69 43
        if ($configuredQuery->getQueryModifier() !== null) {
70 1
            $analysedQuery->setQueryModifier($configuredQuery->getQueryModifier());
71
        }
72
73 43
        return $analysedQuery;
74
    }
75
76
    /**
77
     * @param QueryBuilder $queryBuilder
78
     * @return null|string
79
     */
80 48
    private function getRootAlias(QueryBuilder $queryBuilder)
81
    {
82 48
        $selectDqlParts = $queryBuilder->getDQLPart('select');
83 48
        if (!isset($selectDqlParts[0])) {
84 2
            return null;
85
        }
86 46
        $select = $selectDqlParts[0];
87 46
        if (!$select instanceof Select) {
88 1
            return null;
89
        }
90 45
        $selectParts = $select->getParts();
91 45
        if (!isset($selectParts[0]) || !is_string($selectParts[0])) {
92
            return null;
93
        }
94 45
        if (mb_strpos($selectParts[0], '(') !== false) {
95 1
            return null;
96
        }
97 44
        if (mb_strpos($selectParts[0], ',') !== false) {
98 1
            return null;
99
        }
100
101 43
        return $selectParts[0];
102
    }
103
104
    /**
105
     * @param ConfiguredQuery $configuredQuery
106
     * @param string $rootAlias
107
     * @param array|OrderingPair[] $orderingPairs
108
     * @return array|OrderingConfiguration[]
109
     */
110 37
    private function mapToOrderingConfigurations(
111
        ConfiguredQuery $configuredQuery,
112
        string $rootAlias,
113
        array $orderingPairs
114
    ): array {
115 37
        $orderingConfigurations = [];
116 37
        $idIncluded = false;
117 37
        $defaultAscending = null;
118 37
        $orderByIdExpression = sprintf('%s.%s', $rootAlias, self::ID_FIELD);
119
120 37
        foreach ($orderingPairs as $orderingPair) {
121 31
            $orderingConfiguration = $configuredQuery->getOrderingConfigurationFor($orderingPair->getOrderBy());
122
123 30
            if ($orderingPair->isOrderingDirectionSet()) {
124 29
                $orderingConfiguration->setOrderAscending($orderingPair->isOrderAscending());
125
            }
126
127 30
            if ($orderingConfiguration->getOrderByExpression() === $orderByIdExpression) {
128 26
                $idIncluded = true;
129
            }
130
131 30
            if ($defaultAscending === null) {
132 30
                $defaultAscending = $orderingConfiguration->isOrderAscending();
133
            }
134
135 30
            $orderingConfigurations[] = $orderingConfiguration;
136
        }
137
138 36
        if ($idIncluded) {
139 26
            return $orderingConfigurations;
140
        }
141
142 10
        $orderingConfigurations[] = (new OrderingConfiguration($orderByIdExpression, self::ID_FIELD))
143 10
            ->setOrderAscending($defaultAscending ?? false)
144
        ;
145
146 10
        return $orderingConfigurations;
147
    }
148
}
149