Passed
Pull Request — master (#206)
by Alex
04:45
created

SerialiseNextPageLinksTrait   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 52
c 1
b 0
f 0
dl 0
loc 167
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
B getNextPageLinkQueryParametersForRootResourceSet() 0 37 8
A needNextPageLink() 0 14 4
A buildNextPageLink() 0 9 1
A getNextLinkUri() 0 22 6
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alex
5
 * Date: 16/02/20
6
 * Time: 1:18 AM
7
 */
8
9
namespace AlgoWeb\PODataLaravel\Serialisers;
10
11
use POData\Common\InvalidOperationException;
12
use POData\Common\ODataConstants;
13
use POData\Common\ODataException;
14
use POData\IService;
15
use POData\ObjectModel\ODataFeed;
16
use POData\ObjectModel\ODataLink;
17
use POData\ObjectModel\ODataURLCollection;
18
use POData\Providers\Metadata\ResourceSetWrapper;
19
use POData\Providers\Query\QueryResult;
20
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode;
21
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
22
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
23
use POData\UriProcessor\RequestDescription;
24
use POData\UriProcessor\SegmentStack;
25
26
trait SerialiseNextPageLinksTrait
27
{
28
    /**
29
     * @param QueryResult $entryObjects
30
     * @param ODataURLCollection|ODataFeed $odata
31
     * @throws InvalidOperationException
32
     * @throws ODataException
33
     */
34
    protected function buildNextPageLink(QueryResult $entryObjects, $odata)
35
    {
36
        $stackSegment = $this->getRequest()->getTargetResourceSetWrapper()->getName();
37
        $lastObject = end($entryObjects->results);
0 ignored issues
show
Bug introduced by
It seems like $entryObjects->results can also be of type null and object; however, parameter $array of end() does only seem to accept array, 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

37
        $lastObject = end(/** @scrutinizer ignore-type */ $entryObjects->results);
Loading history...
38
        $segment = $this->getNextLinkUri($lastObject);
39
        $nextLink = new ODataLink();
40
        $nextLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING;
41
        $nextLink->url = rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment;
42
        $odata->nextPageLink = $nextLink;
43
    }
44
45
    /**
46
     * Wheter next link is needed for the current resource set (feed)
47
     * being serialized.
48
     *
49
     * @param int $resultSetCount Number of entries in the current
50
     *                            resource set
51
     *
52
     * @return bool true if the feed must have a next page link
53
     * @throws InvalidOperationException
54
     */
55
    protected function needNextPageLink($resultSetCount)
56
    {
57
        $currentResourceSet = $this->getCurrentResourceSetWrapper();
58
        $recursionLevel = count($this->getStack()->getSegmentNames());
59
        $pageSize = $currentResourceSet->getResourceSetPageSize();
60
61
        if (1 == $recursionLevel) {
62
            //presence of $top option affect next link for root container
63
            $topValueCount = $this->getRequest()->getTopOptionCount();
64
            if (null !== $topValueCount && ($topValueCount <= $pageSize)) {
65
                return false;
66
            }
67
        }
68
        return $resultSetCount == $pageSize;
69
    }
70
71
72
    /**
73
     * Get next page link from the given entity instance.
74
     *
75
     * @param  mixed          &$lastObject Last object serialized to be
76
     *                                     used for generating
77
     *                                     $skiptoken
78
     * @throws ODataException
79
     * @return string         for the link for next page
80
     * @throws InvalidOperationException
81
     */
82
    protected function getNextLinkUri(&$lastObject)
83
    {
84
        $currentExpandedProjectionNode = $this->getCurrentExpandedProjectionNode();
85
        $internalOrderByInfo = $currentExpandedProjectionNode->getInternalOrderByInfo();
86
        if (null === $internalOrderByInfo) {
87
            throw new InvalidOperationException('Null');
88
        }
89
        if (!$internalOrderByInfo instanceof InternalOrderByInfo) {
0 ignored issues
show
introduced by
$internalOrderByInfo is always a sub-type of POData\UriProcessor\Quer...ser\InternalOrderByInfo.
Loading history...
90
            throw new InvalidOperationException(get_class($internalOrderByInfo));
91
        }
92
        $numSegments = count($internalOrderByInfo->getOrderByPathSegments());
93
        $queryParameterString = $this->getNextPageLinkQueryParametersForRootResourceSet();
94
95
        $skipToken = $internalOrderByInfo->buildSkipTokenValue($lastObject);
96
        if (empty($skipToken)) {
97
            throw new InvalidOperationException('!is_null($skipToken)');
98
        }
99
        $token = (1 < $numSegments) ? '$skiptoken=' : '$skip=';
100
        $skipToken = (1 < $numSegments) ? $skipToken : intval(trim($skipToken, '\''));
101
        $skipToken = '?' . $queryParameterString . $token . $skipToken;
102
103
        return $skipToken;
104
    }
105
106
    /**
107
     * Builds the string corresponding to query parameters for top level results
108
     * (result set identified by the resource path) to be put in next page link.
109
     *
110
     * @return string|null string representing the query parameters in the URI
111
     *                     query parameter format, NULL if there
112
     *                     is no query parameters
113
     *                     required for the next link of top level result set
114
     * @throws InvalidOperationException
115
     */
116
    protected function getNextPageLinkQueryParametersForRootResourceSet()
117
    {
118
        /** @var string|null $queryParameterString */
119
        $queryParameterString = null;
120
        foreach ([ODataConstants::HTTPQUERY_STRING_FILTER,
121
                     ODataConstants::HTTPQUERY_STRING_EXPAND,
122
                     ODataConstants::HTTPQUERY_STRING_ORDERBY,
123
                     ODataConstants::HTTPQUERY_STRING_INLINECOUNT,
124
                     ODataConstants::HTTPQUERY_STRING_SELECT, ] as $queryOption) {
125
            /** @var string|null $value */
126
            $value = $this->getService()->getHost()->getQueryStringItem($queryOption);
127
            if (null !== $value) {
128
                if (null !== $queryParameterString) {
129
                    $queryParameterString = /** @scrutinizer ignore-type */$queryParameterString . '&';
130
                }
131
132
                $queryParameterString .= $queryOption . '=' . $value;
133
            }
134
        }
135
136
        $topCountValue = $this->getRequest()->getTopOptionCount();
137
        if (null !== $topCountValue) {
138
            $remainingCount = $topCountValue-$this->getRequest()->getTopCount();
139
            if (0 < $remainingCount) {
140
                if (null !== $queryParameterString) {
141
                    $queryParameterString .= '&';
142
                }
143
144
                $queryParameterString .= ODataConstants::HTTPQUERY_STRING_TOP . '=' . $remainingCount;
145
            }
146
        }
147
148
        if (null !== $queryParameterString) {
149
            $queryParameterString .= '&';
150
        }
151
152
        return $queryParameterString;
153
    }
154
155
    /**
156
     * Gets reference to the request submitted by client.
157
     *
158
     * @return RequestDescription
159
     * @throws InvalidOperationException
160
     */
161
    abstract public function getRequest();
162
163
    /**
164
     * Gets the segment stack instance.
165
     *
166
     * @return SegmentStack
167
     */
168
    abstract public function getStack();
169
170
    /**
171
     * Resource set wrapper for the resource being serialized.
172
     *
173
     * @return ResourceSetWrapper
174
     * @throws InvalidOperationException
175
     */
176
    abstract protected function getCurrentResourceSetWrapper();
177
178
    /**
179
     * Find a 'ExpandedProjectionNode' instance in the projection tree
180
     * which describes the current segment.
181
     *
182
     * @return null|RootProjectionNode|ExpandedProjectionNode
183
     * @throws InvalidOperationException
184
     */
185
    abstract protected function getCurrentExpandedProjectionNode();
186
187
    /**
188
     * Gets the data service instance.
189
     *
190
     * @return IService
191
     */
192
    abstract public function getService();
193
}
194