Manager::getRequestedIncludes()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * This file is part of the League\Fractal package.
5
 *
6
 * (c) Phil Sturgeon <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\Fractal;
13
14
use League\Fractal\Resource\ResourceInterface;
15
use League\Fractal\Serializer\DataArraySerializer;
16
use League\Fractal\Serializer\SerializerAbstract;
17
18
/**
19
 * Manager
20
 *
21
 * Not a wildly creative name, but the manager is what a Fractal user will interact
22
 * with the most. The manager has various configurable options, and allows users
23
 * to create the "root scope" easily.
24
 */
25
class Manager
26
{
27
    /**
28
     * Array of scope identifiers for resources to include.
29
     *
30
     * @var array
31
     */
32
    protected $requestedIncludes = [];
33
34
    /**
35
     * Array of scope identifiers for resources to exclude.
36
     *
37
     * @var array
38
     */
39
    protected $requestedExcludes = [];
40
41
    /**
42
     * Array of requested fieldsets.
43
     *
44
     * @var array
45
     */
46
    protected $requestedFieldsets = [];
47
48
    /**
49
     * Array containing modifiers as keys and an array value of params.
50
     *
51
     * @var array
52
     */
53
    protected $includeParams = [];
54
55
    /**
56
     * The character used to separate modifier parameters.
57
     *
58
     * @var string
59
     */
60
    protected $paramDelimiter = '|';
61
62
    /**
63
     * Upper limit to how many levels of included data are allowed.
64
     *
65
     * @var int
66
     */
67
    protected $recursionLimit = 10;
68
69
    /**
70
     * Serializer.
71
     *
72
     * @var SerializerAbstract
73
     */
74
    protected $serializer;
75
76
    /**
77
     * Factory used to create new configured scopes.
78
     *
79
     * @var ScopeFactoryInterface
80
     */
81
    private $scopeFactory;
82
83 86
    public function __construct(ScopeFactoryInterface $scopeFactory = null)
84
    {
85 86
        $this->scopeFactory = $scopeFactory ?: new ScopeFactory();
86 86
    }
87
88
    /**
89
     * Create Data.
90
     *
91
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
92
     *
93
     * @param ResourceInterface $resource
94
     * @param string            $scopeIdentifier
95
     * @param Scope             $parentScopeInstance
96
     *
97
     * @return Scope
98
     */
99 42
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
100
    {
101 42
        if ($parentScopeInstance !== null) {
102 36
            return $this->scopeFactory->createChildScopeFor($this, $parentScopeInstance, $resource, $scopeIdentifier);
103
        }
104
105 6
        return $this->scopeFactory->createScopeFor($this, $resource, $scopeIdentifier);
106
    }
107
108
    /**
109
     * Get Include Params.
110
     *
111
     * @param string $include
112
     *
113
     * @return \League\Fractal\ParamBag
114
     */
115 32
    public function getIncludeParams($include)
116
    {
117 32
        $params = isset($this->includeParams[$include]) ? $this->includeParams[$include] : [];
118
119 32
        return new ParamBag($params);
120
    }
121
122
    /**
123
     * Get Requested Includes.
124
     *
125
     * @return array
126
     */
127 46
    public function getRequestedIncludes()
128
    {
129 46
        return $this->requestedIncludes;
130
    }
131
132
    /**
133
     * Get Requested Excludes.
134
     *
135
     * @return array
136
     */
137 33
    public function getRequestedExcludes()
138
    {
139 33
        return $this->requestedExcludes;
140
    }
141
142
    /**
143
     * Get Serializer.
144
     *
145
     * @return SerializerAbstract
146
     */
147 60
    public function getSerializer()
148
    {
149 60
        if (! $this->serializer) {
150 10
            $this->setSerializer(new DataArraySerializer());
151 10
        }
152
153 60
        return $this->serializer;
154
    }
155
156
    /**
157
     * Parse Include String.
158
     *
159
     * @param array|string $includes Array or csv string of resources to include
160
     *
161
     * @return $this
162
     */
163 44
    public function parseIncludes($includes)
164
    {
165
        // Wipe these before we go again
166 44
        $this->requestedIncludes = $this->includeParams = [];
167
168 44
        if (is_string($includes)) {
169 34
            $includes = explode(',', $includes);
170 34
        }
171
172 44
        if (! is_array($includes)) {
173 2
            throw new \InvalidArgumentException(
174 2
                'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
175 2
            );
176
        }
177
178 42
        foreach ($includes as $include) {
179 42
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
180
181
            // Trim it down to a cool level of recursion
182 42
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);
183
184 42
            if (in_array($includeName, $this->requestedIncludes)) {
185 1
                continue;
186
            }
187 42
            $this->requestedIncludes[] = $includeName;
188
189
            // No Params? Bored
190 42
            if ($allModifiersStr === null) {
191 41
                continue;
192
            }
193
194
            // Matches multiple instances of 'something(foo|bar|baz)' in the string
195
            // I guess it ignores : so you could use anything, but probably don't do that
196 2
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
197
198
            // [0] is full matched strings...
199 2
            $modifierCount = count($allModifiersArr[0]);
200
201 2
            $modifierArr = [];
202
203 2
            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
204
                // [1] is the modifier
205 2
                $modifierName = $allModifiersArr[1][$modifierIt];
206
207
                // and [3] is delimited params
208 2
                $modifierParamStr = $allModifiersArr[3][$modifierIt];
209
210
                // Make modifier array key with an array of params as the value
211 2
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
212 2
            }
213
214 2
            $this->includeParams[$includeName] = $modifierArr;
215 42
        }
216
217
        // This should be optional and public someday, but without it includes would never show up
218 42
        $this->autoIncludeParents();
219
220 42
        return $this;
221
    }
222
223
    /**
224
     * Parse field parameter.
225
     *
226
     * @param array $fieldsets Array of fields to include. It must be an array whose keys
227
     *                         are resource types and values an array or a string
228
     *                         of the fields to return, separated by a comma
229
     *
230
     * @return $this
231
     */
232 13
    public function parseFieldsets(array $fieldsets)
233
    {
234 13
        $this->requestedFieldsets = [];
235 13
        foreach ($fieldsets as $type => $fields) {
236 13
            if (is_string($fields)) {
237 13
                $fields = explode(',', $fields);
238 13
            }
239
240
            //Remove empty and repeated fields
241 13
            $this->requestedFieldsets[$type] = array_unique(array_filter($fields));
242 13
        }
243 13
        return $this;
244
    }
245
246
    /**
247
     * Get requested fieldsets.
248
     *
249
     * @return array
250
     */
251 1
    public function getRequestedFieldsets()
252
    {
253 1
        return $this->requestedFieldsets;
254
    }
255
256
    /**
257
     * Get fieldset params for the specified type.
258
     *
259
     * @param string $type
260
     *
261
     * @return \League\Fractal\ParamBag|null
262
     */
263 59
    public function getFieldset($type)
264
    {
265 59
        return !isset($this->requestedFieldsets[$type]) ?
266 59
            null :
267 59
            new ParamBag($this->requestedFieldsets[$type]);
268
    }
269
270
    /**
271
     * Parse Exclude String.
272
     *
273
     * @param array|string $excludes Array or csv string of resources to exclude
274
     *
275
     * @return $this
276
     */
277 5
    public function parseExcludes($excludes)
278
    {
279 5
        $this->requestedExcludes = [];
280
281 5
        if (is_string($excludes)) {
282 2
            $excludes = explode(',', $excludes);
283 2
        }
284
285 5
        if (! is_array($excludes)) {
286 2
            throw new \InvalidArgumentException(
287 2
                'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
288 2
            );
289
        }
290
291 3
        foreach ($excludes as $excludeName) {
292 3
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
293
294 3
            if (in_array($excludeName, $this->requestedExcludes)) {
295 1
                continue;
296
            }
297
298 3
            $this->requestedExcludes[] = $excludeName;
299 3
        }
300
301 3
        return $this;
302
    }
303
304
    /**
305
     * Set Recursion Limit.
306
     *
307
     * @param int $recursionLimit
308
     *
309
     * @return $this
310
     */
311 1
    public function setRecursionLimit($recursionLimit)
312
    {
313 1
        $this->recursionLimit = $recursionLimit;
314
315 1
        return $this;
316
    }
317
318
    /**
319
     * Set Serializer
320
     *
321
     * @param SerializerAbstract $serializer
322
     *
323
     * @return $this
324
     */
325 60
    public function setSerializer(SerializerAbstract $serializer)
326
    {
327 60
        $this->serializer = $serializer;
328
329 60
        return $this;
330
    }
331
332
    /**
333
     * Auto-include Parents
334
     *
335
     * Look at the requested includes and automatically include the parents if they
336
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
337
     *
338
     * @internal
339
     *
340
     * @return void
341
     */
342 42
    protected function autoIncludeParents()
343
    {
344 42
        $parsed = [];
345
346 42
        foreach ($this->requestedIncludes as $include) {
347 42
            $nested = explode('.', $include);
348
349 42
            $part = array_shift($nested);
350 42
            $parsed[] = $part;
351
352 42
            while (count($nested) > 0) {
353 11
                $part .= '.'.array_shift($nested);
354 11
                $parsed[] = $part;
355 11
            }
356 42
        }
357
358 42
        $this->requestedIncludes = array_values(array_unique($parsed));
359 42
    }
360
361
    /**
362
     * Trim to Acceptable Recursion Level
363
     *
364
     * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
365
     * by trains or whatever the hell that movie was about.
366
     *
367
     * @internal
368
     *
369
     * @param string $includeName
370
     *
371
     * @return string
372
     */
373 44
    protected function trimToAcceptableRecursionLevel($includeName)
374
    {
375 44
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
376
    }
377
}
378