Passed
Push — master ( c39fb6...9e9a0d )
by Rafael
04:42
created

DoctrineOffsetCursorPaginator::applyCursor()   C

Complexity

Conditions 14
Paths 120

Size

Total Lines 54
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 16.7571

Importance

Changes 0
Metric Value
cc 14
eloc 32
nc 120
nop 3
dl 0
loc 54
ccs 22
cts 29
cp 0.7586
crap 16.7571
rs 6.066
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Pagination;
12
13
use Doctrine\ORM\QueryBuilder;
14
use Ynlo\GraphQLBundle\Model\ConnectionInterface;
15
use Ynlo\GraphQLBundle\Model\NodeConnection;
16
17
/**
18
 * DoctrineOffsetCursorPaginator
19
 */
20
class DoctrineOffsetCursorPaginator implements DoctrineCursorPaginatorInterface
21
{
22
    /**
23
     * @var NodeConnection
24
     */
25
    protected $connection;
26
27
    /**
28
     * {@inheritdoc}
29
     */
30 10
    public function paginate(QueryBuilder $query, PaginationRequest $pagination, ConnectionInterface $connection)
31
    {
32 10
        $count = $this->getQueryTotal($query);
33 10
        $this->connection = $connection;
0 ignored issues
show
Documentation Bug introduced by
$connection is of type Ynlo\GraphQLBundle\Model\ConnectionInterface, but the property $connection was declared to be of type Ynlo\GraphQLBundle\Model\NodeConnection. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
34 10
        $this->connection->setTotalCount($count);
35
36 10
        $this->applyCursor($query, $count, $pagination);
37
38 10
        $limit = $pagination->getFirst() ?? $pagination->getLast();
39 10
        $query->setMaxResults($limit);
40
41 10
        $results = $query->getQuery()->execute();
42
43 10
        $offset = $query->getFirstResult();
44
45 10
        $cursorOffset = $offset - 1;
46 10
        foreach ($results as $result) {
47 8
            $cursorOffset ++;
48
49 8
            if (!$this->connection->getPageInfo()->getStartCursor()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->connection->getPa...nfo()->getStartCursor() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
50 8
                $this->connection->getPageInfo()->setStartCursor($this->encodeCursor($offset));
51
            }
52
53 8
            $cursor = $this->encodeCursor($cursorOffset);
54 8
            $this->connection->addEdge($this->connection->createEdge($result, $cursor));
55 8
            $this->connection->getPageInfo()->setEndCursor($cursor);
56
        }
57 10
    }
58
59
    /**
60
     * @param QueryBuilder $qb
61
     *
62
     * @return int
63
     */
64 10
    protected function getQueryTotal(QueryBuilder $qb)
65
    {
66 10
        $countQuery = clone $qb;
67
68 10
        if (count($qb->getParameters()) > 0) {
69 3
            $countQuery->setParameters($qb->getParameters());
70
        }
71
72 10
        if ($countQuery->getDQLPart('orderBy')) {
73 7
            $countQuery->resetDQLPart('orderBy');
74
        }
75
76 10
        $countQuery->setMaxResults(null);
77 10
        $countQuery->setFirstResult(0);
78
79 10
        $queryAlias = $qb->getAllAliases()[0];
80 10
        $countQuery->select(sprintf('count(DISTINCT %s.%s) as total', $queryAlias, 'id'));
81
82 10
        return $countQuery->getQuery()->getSingleScalarResult();
83
    }
84
85
    /**
86
     * @param QueryBuilder      $qb
87
     * @param int               $count
88
     * @param PaginationRequest $pagination
89
     */
90 10
    protected function applyCursor(QueryBuilder $qb, $count, PaginationRequest $pagination)
91
    {
92 10
        $limit = $pagination->getFirst() ?? $pagination->getLast();
93 10
        $offset = 0;
94 10
        if (null !== $pagination->getBefore()) {
95 2
            $offset = $this->decodeCursor($pagination->getBefore()) - $limit;
96
97
            //when the offset is less than 0,
98
            //the limit of records will be modified to start in 0
99 2
            if ($offset < 0) {
100
                if ($pagination->getFirst()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pagination->getFirst() of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
101
                    $pagination->setFirst($pagination->getFirst() - abs($offset));
0 ignored issues
show
Bug introduced by
$pagination->getFirst() - abs($offset) of type double is incompatible with the type integer expected by parameter $first of Ynlo\GraphQLBundle\Pagin...tionRequest::setFirst(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
                    $pagination->setFirst(/** @scrutinizer ignore-type */ $pagination->getFirst() - abs($offset));
Loading history...
102
                } else {
103
                    $pagination->setLast($pagination->getLast() - abs($offset));
0 ignored issues
show
Bug introduced by
$pagination->getLast() - abs($offset) of type double is incompatible with the type integer expected by parameter $last of Ynlo\GraphQLBundle\Pagin...ationRequest::setLast(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
                    $pagination->setLast(/** @scrutinizer ignore-type */ $pagination->getLast() - abs($offset));
Loading history...
104
                }
105
                $offset = 0;
106
            }
107
108
            //first records before any cursor always start in 0
109 2
            if ($pagination->getFirst()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pagination->getFirst() of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
110 2
                $offset = 0;
111
            }
112 8
        } elseif (null !== $pagination->getAfter()) {
113
            //last records after any cursor always start in ($count - $limit)
114 2
            if ($pagination->getLast()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pagination->getLast() of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
115 1
                $offset = $count - $pagination->getLast();
116 1
                if ($offset < $pagination->getAfter()) {
117 1
                    $offset = $pagination->getAfter() + 1;
118
                }
119
            } else {
120 1
                $offset = $this->decodeCursor($pagination->getAfter()) + 1;
121
            }
122
        }
123
124 10
        if ($pagination->getLast() && !$pagination->getBefore() && !$pagination->getAfter()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pagination->getLast() of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $pagination->getAfter() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $pagination->getBefore() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
125
            $offset = $count - $pagination->getLast();
126
            if ($offset < 0) {
127
                $offset = 0;
128
            }
129
        }
130
131 10
        if (0 === $offset) {
132 7
            $this->connection->getPageInfo()->setHasPreviousPage(false);
133
        } else {
134 3
            $this->connection->getPageInfo()->setHasPreviousPage(true);
135
        }
136
137 10
        if ($offset + $limit >= $count) {
138 4
            $this->connection->getPageInfo()->setHasNextPage(false);
139
        } else {
140 7
            $this->connection->getPageInfo()->setHasNextPage(true);
141
        }
142
143 10
        $qb->setFirstResult($offset);
144 10
    }
145
146
    /**
147
     * @param string $cursor
148
     *
149
     * @return int
150
     */
151 3
    protected function decodeCursor($cursor)
152
    {
153 3
        list(, $offset) = explode(':', base64_decode($cursor));
0 ignored issues
show
Bug introduced by
It seems like base64_decode($cursor) can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
        list(, $offset) = explode(':', /** @scrutinizer ignore-type */ base64_decode($cursor));
Loading history...
154
155 3
        return $offset;
156
    }
157
158
    /**
159
     * @param string $offset
160
     *
161
     * @return string
162
     */
163 8
    protected function encodeCursor($offset)
164
    {
165 8
        return base64_encode(sprintf('cursor:%s', $offset));
166
    }
167
}
168