Completed
Pull Request — master (#306)
by Benoît
33:19
created

Manager::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 2
eloc 2
nc 2
nop 1
crap 2
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 containing modifiers as keys and an array value of params.
43
     *
44
     * @var array
45
     */
46
    protected $includeParams = [];
47
48
    /**
49
     * The character used to separate modifier parameters.
50
     *
51
     * @var string
52
     */
53
    protected $paramDelimiter = '|';
54
55
    /**
56
     * Upper limit to how many levels of included data are allowed.
57
     *
58
     * @var int
59
     */
60
    protected $recursionLimit = 10;
61
62
    /**
63
     * Serializer.
64
     *
65
     * @var SerializerAbstract
66
     */
67
    protected $serializer;
68
69
    /**
70
     * Factory used to create new configured scopes.
71
     *
72
     * @var ScopeFactoryInterface
73
     */
74
    private $scopeFactory;
75
76 69
    public function __construct(ScopeFactoryInterface $scopeFactory = null)
77
    {
78 69
        $this->scopeFactory = $scopeFactory ?: new ScopeFactory();
79 69
    }
80
81
    /**
82
     * Create Data.
83
     *
84
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
85
     *
86
     * @param ResourceInterface $resource
87
     * @param string            $scopeIdentifier
88
     * @param Scope             $parentScopeInstance
89
     *
90
     * @return Scope
91
     */
92 34
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
93
    {
94 34
        if ($parentScopeInstance !== null) {
95 29
            return $this->scopeFactory->createChildScopeFor($this, $parentScopeInstance, $resource, $scopeIdentifier);
96
        }
97
98 5
        return $this->scopeFactory->createScopeFor($this, $resource, $scopeIdentifier);
99
    }
100
101
    /**
102
     * Get Include Params.
103
     *
104
     * @param string $include
105
     *
106
     * @return \League\Fractal\ParamBag
107
     */
108 25
    public function getIncludeParams($include)
109
    {
110 25
        $params = isset($this->includeParams[$include]) ? $this->includeParams[$include] : [];
111
112 25
        return new ParamBag($params);
113
    }
114
115
    /**
116
     * Get Requested Includes.
117
     *
118
     * @return array
119
     */
120 38
    public function getRequestedIncludes()
121
    {
122 38
        return $this->requestedIncludes;
123
    }
124
125
    /**
126
     * Get Requested Excludes.
127
     *
128
     * @return array
129
     */
130 26
    public function getRequestedExcludes()
131
    {
132 26
        return $this->requestedExcludes;
133
    }
134
135
    /**
136
     * Get Serializer.
137
     *
138
     * @return SerializerAbstract
139
     */
140 45
    public function getSerializer()
141
    {
142 45
        if (! $this->serializer) {
143 7
            $this->setSerializer(new DataArraySerializer());
144 7
        }
145
146 45
        return $this->serializer;
147
    }
148
149
    /**
150
     * Parse Include String.
151
     *
152
     * @param array|string $includes Array or csv string of resources to include
153
     *
154
     * @return $this
155
     */
156 37
    public function parseIncludes($includes)
157
    {
158
        // Wipe these before we go again
159 37
        $this->requestedIncludes = $this->includeParams = [];
160
161 37
        if (is_string($includes)) {
162 31
            $includes = explode(',', $includes);
163 31
        }
164
165 37
        if (! is_array($includes)) {
166 2
            throw new \InvalidArgumentException(
167 2
                'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
168 2
            );
169
        }
170
171 35
        foreach ($includes as $include) {
172 35
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
173
174
            // Trim it down to a cool level of recursion
175 35
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);
176
177 35
            if (in_array($includeName, $this->requestedIncludes)) {
178 1
                continue;
179
            }
180 35
            $this->requestedIncludes[] = $includeName;
181
182
            // No Params? Bored
183 35
            if ($allModifiersStr === null) {
184 34
                continue;
185
            }
186
187
            // Matches multiple instances of 'something(foo|bar|baz)' in the string
188
            // I guess it ignores : so you could use anything, but probably don't do that
189 2
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
190
191
            // [0] is full matched strings...
192 2
            $modifierCount = count($allModifiersArr[0]);
193
194 2
            $modifierArr = [];
195
196 2
            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
197
                // [1] is the modifier
198 2
                $modifierName = $allModifiersArr[1][$modifierIt];
199
200
                // and [3] is delimited params
201 2
                $modifierParamStr = $allModifiersArr[3][$modifierIt];
202
203
                // Make modifier array key with an array of params as the value
204 2
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
205 2
            }
206
207 2
            $this->includeParams[$includeName] = $modifierArr;
208 35
        }
209
210
        // This should be optional and public someday, but without it includes would never show up
211 35
        $this->autoIncludeParents();
212
213 35
        return $this;
214
    }
215
216
    /**
217
     * Parse Exclude String.
218
     *
219
     * @param array|string $excludes Array or csv string of resources to exclude
220
     *
221
     * @return $this
222
     */
223 5
    public function parseExcludes($excludes)
224
    {
225 5
        $this->requestedExcludes = [];
226
227 5
        if (is_string($excludes)) {
228 2
            $excludes = explode(',', $excludes);
229 2
        }
230
231 5
        if (! is_array($excludes)) {
232 2
            throw new \InvalidArgumentException(
233 2
                'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
234 2
            );
235
        }
236
237 3
        foreach ($excludes as $excludeName) {
238 3
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
239
240 3
            if (in_array($excludeName, $this->requestedExcludes)) {
241 1
                continue;
242
            }
243
244 3
            $this->requestedExcludes[] = $excludeName;
245 3
        }
246
247 3
        return $this;
248
    }
249
250
    /**
251
     * Set Recursion Limit.
252
     *
253
     * @param int $recursionLimit
254
     *
255
     * @return $this
256
     */
257 1
    public function setRecursionLimit($recursionLimit)
258
    {
259 1
        $this->recursionLimit = $recursionLimit;
260
261 1
        return $this;
262
    }
263
264
    /**
265
     * Set Serializer
266
     *
267
     * @param SerializerAbstract $serializer
268
     *
269
     * @return $this
270
     */
271 45
    public function setSerializer(SerializerAbstract $serializer)
272
    {
273 45
        $this->serializer = $serializer;
274
275 45
        return $this;
276
    }
277
278
    /**
279
     * Auto-include Parents
280
     *
281
     * Look at the requested includes and automatically include the parents if they
282
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
283
     *
284
     * @internal
285
     *
286
     * @return void
287
     */
288 35
    protected function autoIncludeParents()
289
    {
290 35
        $parsed = [];
291
292 35
        foreach ($this->requestedIncludes as $include) {
293 35
            $nested = explode('.', $include);
294
295 35
            $part = array_shift($nested);
296 35
            $parsed[] = $part;
297
298 35
            while (count($nested) > 0) {
299 7
                $part .= '.'.array_shift($nested);
300 7
                $parsed[] = $part;
301 7
            }
302 35
        }
303
304 35
        $this->requestedIncludes = array_values(array_unique($parsed));
305 35
    }
306
307
    /**
308
     * Trim to Acceptable Recursion Level
309
     *
310
     * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
311
     * by trains or whatever the hell that movie was about.
312
     *
313
     * @internal
314
     *
315
     * @param string $includeName
316
     *
317
     * @return string
318
     */
319 37
    protected function trimToAcceptableRecursionLevel($includeName)
320
    {
321 37
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
322
    }
323
}
324