Passed
Pull Request — master (#206)
by Alex
06:35 queued 01:20
created

SerialiseNextPageLinksTrait::needNextPageLink()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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