Completed
Push — master ( 34b7ec...a579ea )
by Alex
13s
created

RootProjectionNode::getEagerLoadList()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 70
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 70
rs 6.8519
c 0
b 0
f 0
cc 7
eloc 38
nc 8
nop 0

How to fix   Long Method   

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
namespace POData\UriProcessor\QueryProcessor\ExpandProjectionParser;
4
5
use POData\Providers\Metadata\ResourceSetWrapper;
6
use POData\Providers\Metadata\ResourceType;
7
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
8
9
/**
10
 * Class RootProjectionNode.
11
 *
12
 * ExpandProjectParser will create a 'Projection Tree' from the $expand
13
 * and/or $select query options, this type is used to represent root of
14
 * the 'Projection Tree', the root holds details about the resource set
15
 * pointed by the resource path uri (ResourceSet, OrderInfo, skip, top,
16
 * pageSize etc..) and flags indicating whether projection and expansions
17
 * are specified.
18
 */
19
class RootProjectionNode extends ExpandedProjectionNode
20
{
21
    const MAX_EXPAND_TREE_DEPTH = 20;
22
23
    /**
24
     * Flag indicates whether expansions were specified in the query or not.
25
     *
26
     * @var bool
27
     */
28
    private $expansionSpecified = false;
29
30
    /**
31
     * Flag indicates whether selections were specified in the query or not.
32
     *
33
     * @var bool
34
     */
35
    private $selectionSpecified = false;
36
37
    /**
38
     * Flag indicates whether any of the expanded resource set is paged or not.
39
     *
40
     * @var bool
41
     */
42
    private $hasPagedExpandedResult = false;
43
44
    /**
45
     * The base resource type of entities identified by the resource path uri,
46
     * this is usually the base resource type of the resource set to which
47
     * the entities belongs to, but it can happen that it's a derived type of
48
     * the resource set base type.
49
     *
50
     * @var ResourceType
51
     */
52
    private $baseResourceType;
53
54
    /**
55
     * Constructs a new instance of 'RootProjectionNode' representing root
56
     * of 'Projection Tree'.
57
     *
58
     * @param ResourceSetWrapper  $resourceSetWrapper  ResourceSetWrapper of
59
     *                                                 the resource pointed
60
     *                                                 by the resource path
61
     * @param InternalOrderByInfo $internalOrderByInfo Details of ordering
62
     *                                                 to be applied to the
63
     *                                                 resource set pointed
64
     *                                                 by the resource path
65
     * @param int                 $skipCount           Number of resources to
66
     *                                                 be skipped from the
67
     *                                                 resource set pointed
68
     *                                                 by the resource path
69
     * @param int                 $takeCount           Number of resources to
70
     *                                                 be taken from the
71
     *                                                 resource set pointed
72
     *                                                 by the resource path
73
     * @param int                 $maxResultCount      The maximum limit
74
     *                                                 configured for the
75
     *                                                 resource set
76
     * @param ResourceType        $baseResourceType    Resource type of the
77
     *                                                 resource pointed
78
     *                                                 by the resource path
79
     */
80
    public function __construct(
81
        ResourceSetWrapper $resourceSetWrapper,
82
        $internalOrderByInfo,
83
        $skipCount,
84
        $takeCount,
85
        $maxResultCount,
86
        ResourceType $baseResourceType
87
    ) {
88
        $this->baseResourceType = $baseResourceType;
89
        parent::__construct(
90
            null,
91
            $resourceSetWrapper,
92
            $internalOrderByInfo,
93
            $skipCount,
94
            $takeCount,
95
            $maxResultCount,
96
            null
97
        );
98
    }
99
100
    /**
101
     * Gets reference to the base resource type of entities identified by
102
     * the resource path uri this is usually the base resource type of the
103
     * resource set to which the entities belongs to but it can happen that
104
     * it's a derived type of the resource set base type.
105
     *
106
     * @return ResourceType
107
     */
108
    public function getResourceType()
109
    {
110
        return $this->baseResourceType;
111
    }
112
113
    /**
114
     * Mark expansions are used in the query or not.
115
     *
116
     * @param bool $isExpansionSpecified True if expansion found, False else
117
     */
118
    public function setExpansionSpecified($isExpansionSpecified = true)
119
    {
120
        $this->expansionSpecified = $isExpansionSpecified;
121
    }
122
123
    /**
124
     * Check whether expansion were specified in the query.
125
     *
126
     * @return bool
127
     */
128
    public function isExpansionSpecified()
129
    {
130
        return $this->expansionSpecified;
131
    }
132
133
    /**
134
     * Mark selections are used in the query or not.
135
     *
136
     * @param bool $isSelectionSpecified True if selection found,
137
     *                                   False else
138
     */
139
    public function setSelectionSpecified($isSelectionSpecified = true)
140
    {
141
        $this->selectionSpecified = $isSelectionSpecified;
142
    }
143
144
    /**
145
     * Check whether selection were specified in the query.
146
     *
147
     * @return bool
148
     */
149
    public function isSelectionSpecified()
150
    {
151
        return $this->selectionSpecified;
152
    }
153
154
    /**
155
     * Mark paged expanded result will be there or not.
156
     *
157
     * @param bool $hasPagedExpandedResult True if found paging on expanded
158
     *                                     result, False else
159
     */
160
    public function setPagedExpandedResult($hasPagedExpandedResult = true)
161
    {
162
        $this->hasPagedExpandedResult = $hasPagedExpandedResult;
163
    }
164
165
    /**
166
     * Check whether any of the expanded resource set is paged.
167
     *
168
     * @return bool
169
     */
170
    public function hasPagedExpandedResult()
171
    {
172
        return $this->hasPagedExpandedResult;
173
    }
174
175
    /**
176
     * Get list of expanded properties to pass to specific query provider for eager loading
177
     *
178
     * @return string[]
179
     */
180
    public function getEagerLoadList()
181
    {
182
        if (!$this->isExpansionSpecified()) {
183
            return [];
184
        }
185
        if (0 === count($this->getChildNodes())) {
186
            return [];
187
        }
188
        // need to use a stack to track chain of parent nodes back to root
189
        // each entry has three elements - zeroth being node itself, first being property name, second is
190
        // index in parent's children - when that overruns parent's childNodes array, we can pop the parent
191
        // node off the stack and move on to grandparent's next child.  When we're finished with a node, then and
192
        // only then generate its relation chain and stash it.  When we're done (stack empty), dedupe the chain stash
193
        // and return it.
194
195
        // set up tracking stack and scratchpad
196
        $trackStack = [];
197
        $trackStack[] = ['node' => $this, 'name' => null, 'index' => 0];
198
        $scratchpad = [];
199
200
        // now start the dance
201
        while (0 < count($trackStack)) {
202
            $stackDex = count($trackStack) - 1;
203
            assert(
204
                self::MAX_EXPAND_TREE_DEPTH > $stackDex,
205
                'Expansion stack too deep - should be less than '. self::MAX_EXPAND_TREE_DEPTH . 'elements'
206
            );
207
            $topNode = $trackStack[$stackDex];
208
            $nodes = $topNode['node']->getChildNodes();
209
            // have we finished processing current level?
210
            // this treats a leaf node as simply another exhausted parent node with all of its zero children having
211
            // been processed
212
            $topDex = $topNode['index'];
213
            if ($topDex >= count($nodes)) {
214
                $eager = '';
215
                foreach ($trackStack as $stack) {
216
                    $eager .= $stack['name'] . '/';
217
                }
218
                $eager = trim($eager, '/');
219
                if (1 < strlen($eager)) {
220
                    $scratchpad[] = $eager;
221
                }
222
                array_pop($trackStack);
223
                assert(
224
                    count($trackStack) === $stackDex,
225
                    'Exhausted node must shrink tracking stack by exactly one element'
226
                );
227
                continue;
228
            }
229
230
            // dig up key
231
            $key = array_keys($nodes)[$topDex];
232
            // prep payload for this child
233
            $payload = ['node' => $nodes[$key], 'name' => $key, 'index' => 0];
234
            array_push($trackStack, $payload);
235
            // advance index pointer on parent
236
            $trackStack[$stackDex]['index']++;
237
            // $stackDex already decrements stack count by 1, so we have to bump it up by two to net out to a +1
238
            assert(
239
                count($trackStack) === $stackDex + 2,
240
                'Non-exhausted node must expand tracking stack by exactly one element'
241
            );
242
        }
243
244
        // dedupe scratchpad
245
        $scratchpad = array_unique($scratchpad);
246
        // deliberately shuffle scratchpad to falsify any ordering assumptions downstream
247
        shuffle($scratchpad);
248
        return $scratchpad;
249
    }
250
}
251