getNextPageLinkQueryParametersForRootResourceSet()   B
last analyzed

Complexity

Conditions 8
Paths 32

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
c 1
b 0
f 0
dl 0
loc 37
rs 8.4444
cc 8
nc 32
nop 0
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Created by PhpStorm.
6
 * User: alex
7
 * Date: 16/02/20
8
 * Time: 1:18 AM.
9
 */
10
namespace AlgoWeb\PODataLaravel\Serialisers;
11
12
use POData\Common\InvalidOperationException;
13
use POData\Common\ODataConstants;
14
use POData\Common\ODataException;
15
use POData\IService;
16
use POData\ObjectModel\ODataFeed;
17
use POData\ObjectModel\ODataLink;
18
use POData\ObjectModel\ODataNextPageLink;
19
use POData\ObjectModel\ODataURLCollection;
20
use POData\Providers\Metadata\ResourceSetWrapper;
21
use POData\Providers\Query\QueryResult;
22
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode;
23
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
24
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
25
use POData\UriProcessor\RequestDescription;
26
use POData\UriProcessor\SegmentStack;
27
28
trait SerialiseNextPageLinksTrait
29
{
30
    /**
31
     * @param  QueryResult                  $entryObjects
32
     * @param  ODataURLCollection|ODataFeed $odata
33
     * @throws InvalidOperationException
34
     * @throws ODataException
35
     */
36
    protected function buildNextPageLink(QueryResult $entryObjects, $odata): void
37
    {
38
        $stackSegment = $this->getRequest()->getTargetResourceSetWrapper()->getName();
39
        /** @var mixed[] $res */
40
        $res                 = $entryObjects->results;
41
        $lastObject          = end($res);
42
        $segment             = $this->getNextLinkUri($lastObject);
43
        $nextLink            = new ODataNextPageLink(ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING);
44
        $nextLink->setUrl(rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment);
45
        $odata->setNextPageLink($nextLink);
46
    }
47
48
    /**
49
     * Wheter next link is needed for the current resource set (feed)
50
     * being serialized.
51
     *
52
     * @param int $resultSetCount Number of entries in the current
53
     *                            resource set
54
     *
55
     * @throws InvalidOperationException
56
     * @return bool                      true if the feed must have a next page link
57
     */
58
    protected function needNextPageLink(int $resultSetCount): bool
59
    {
60
        $currentResourceSet = $this->getCurrentResourceSetWrapper();
61
        $recursionLevel     = count($this->getStack()->getSegmentNames());
62
        $pageSize           = $currentResourceSet->getResourceSetPageSize();
63
64
        if (1 == $recursionLevel) {
65
            //presence of $top option affect next link for root container
66
            $topValueCount = $this->getRequest()->getTopOptionCount();
67
            if (null !== $topValueCount && ($topValueCount <= $pageSize)) {
68
                return false;
69
            }
70
        }
71
        return $resultSetCount == $pageSize;
72
    }
73
74
75
    /**
76
     * Get next page link from the given entity instance.
77
     *
78
     * @param  mixed                     $lastObject  Last object serialized to be
79
     *                                                used for generating
80
     *                                                $skiptoken
81
     * @throws ODataException
82
     * @throws InvalidOperationException
83
     * @return string                    for the link for next page
84
     */
85
    protected function getNextLinkUri(&$lastObject)
86
    {
87
        /** @var RootProjectionNode|ExpandedProjectionNode $currentExpandedProjectionNode */
88
        $currentExpandedProjectionNode = $this->getCurrentExpandedProjectionNode();
89
        $internalOrderByInfo           = $currentExpandedProjectionNode->getInternalOrderByInfo();
90
        if (null === $internalOrderByInfo) {
91
            throw new InvalidOperationException('Null');
92
        }
93
        $numSegments          = count($internalOrderByInfo->getOrderByPathSegments());
94
        $queryParameterString = $this->getNextPageLinkQueryParametersForRootResourceSet();
95
96
        $skipToken = $internalOrderByInfo->buildSkipTokenValue($lastObject);
97
        if (empty($skipToken)) {
98
            throw new InvalidOperationException('!is_null($skipToken)');
99
        }
100
        $token     = (1 < $numSegments) ? '$skiptoken=' : '$skip=';
101
        $skipToken = (1 < $numSegments) ? $skipToken : intval(trim($skipToken, '\''));
102
        $skipToken = '?' . $queryParameterString . $token . $skipToken;
103
104
        return $skipToken;
105
    }
106
107
    /**
108
     * Builds the string corresponding to query parameters for top level results
109
     * (result set identified by the resource path) to be put in next page link.
110
     *
111
     * @throws InvalidOperationException
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
     */
117
    protected function getNextPageLinkQueryParametersForRootResourceSet(): ?string
118
    {
119
        /** @var string|null $queryParameterString */
120
        $queryParameterString = null;
121
        foreach ([ODataConstants::HTTPQUERY_STRING_FILTER,
122
            ODataConstants::HTTPQUERY_STRING_EXPAND,
123
            ODataConstants::HTTPQUERY_STRING_ORDERBY,
124
            ODataConstants::HTTPQUERY_STRING_INLINECOUNT,
125
            ODataConstants::HTTPQUERY_STRING_SELECT, ] as $queryOption) {
126
            /** @var string|null $value */
127
            $value = $this->getService()->getHost()->getQueryStringItem($queryOption);
128
            if (null !== $value) {
129
                if (null !== $queryParameterString) {
130
                    $queryParameterString = /* @scrutinizer ignore-type */$queryParameterString . '&';
131
                }
132
133
                $queryParameterString .= $queryOption . '=' . $value;
134
            }
135
        }
136
137
        $topCountValue = $this->getRequest()->getTopOptionCount();
138
        if (null !== $topCountValue) {
139
            $remainingCount = $topCountValue - $this->getRequest()->getTopCount();
140
            if (0 < $remainingCount) {
141
                if (null !== $queryParameterString) {
142
                    $queryParameterString .= '&';
143
                }
144
145
                $queryParameterString .= ODataConstants::HTTPQUERY_STRING_TOP . '=' . $remainingCount;
146
            }
147
        }
148
149
        if (null !== $queryParameterString) {
150
            $queryParameterString .= '&';
151
        }
152
153
        return $queryParameterString;
154
    }
155
156
    /**
157
     * Gets reference to the request submitted by client.
158
     *
159
     * @throws InvalidOperationException
160
     * @return RequestDescription
161
     */
162
    abstract public function getRequest();
163
164
    /**
165
     * Gets the segment stack instance.
166
     *
167
     * @return SegmentStack
168
     */
169
    abstract public function getStack();
170
171
    /**
172
     * Resource set wrapper for the resource being serialized.
173
     *
174
     * @throws InvalidOperationException
175
     * @return ResourceSetWrapper
176
     */
177
    abstract protected function getCurrentResourceSetWrapper(): ResourceSetWrapper;
178
179
    /**
180
     * Find a 'ExpandedProjectionNode' instance in the projection tree
181
     * which describes the current segment.
182
     *
183
     * @throws InvalidOperationException
184
     * @return null|RootProjectionNode|ExpandedProjectionNode
185
     */
186
    abstract protected function getCurrentExpandedProjectionNode();
187
188
    /**
189
     * Gets the data service instance.
190
     *
191
     * @return IService
192
     */
193
    abstract public function getService();
194
}
195