Completed
Push — master ( 74fef5...198b3f )
by Nate
02:04
created

Scope::includeValue()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
rs 8.8571
cc 5
eloc 6
nc 3
nop 2
1
<?php
2
3
/**
4
 * @package   Transform
5
 * @author    Flipbox Factory
6
 * @copyright Copyright (c) 2017, Flipbox Digital
7
 * @link      https://github.com/flipbox/transform/releases/latest
8
 * @license   https://github.com/flipbox/transform/blob/master/LICENSE
9
 */
10
11
namespace flipbox\transform;
12
13
use flipbox\transform\resources\CollectionInterface;
14
use flipbox\transform\resources\ItemInterface;
15
use flipbox\transform\resources\ResourceInterface;
16
use flipbox\transform\transformers\TransformerInterface;
17
18
/**
19
 * @package flipbox\transform
20
 * @author Flipbox Factory <[email protected]>
21
 * @since 1.0.0
22
 */
23
class Scope
24
{
25
26
    /**
27
     * @var string
28
     */
29
    protected $scopeIdentifier;
30
31
    /**
32
     * @var Factory
33
     */
34
    protected $factory;
35
36
    /**
37
     * @var ResourceInterface
38
     */
39
    protected $resource;
40
41
    /**
42
     * @var array
43
     */
44
    protected $parentScopes = [];
45
46
    /**
47
     * Scope constructor.
48
     * @param Factory $factory
49
     * @param ResourceInterface $resource
50
     * @param string|null $scopeIdentifier
51
     * @param array $parentScopes
52
     */
53
    public function __construct(Factory $factory, ResourceInterface $resource, string $scopeIdentifier = null, array $parentScopes = [])
54
    {
55
        $this->factory = $factory;
56
        $this->resource = $resource;
57
        $this->scopeIdentifier = $scopeIdentifier;
58
        $this->parentScopes = $parentScopes;
59
    }
60
61
    /**
62
     * @return ResourceInterface
63
     */
64
    public function getResource(): ResourceInterface
65
    {
66
        return $this->resource;
67
    }
68
69
    /**
70
     * Getter for manager.
71
     *
72
     * @return Factory
73
     */
74
    public function getFactory(): Factory
75
    {
76
        return $this->factory;
77
    }
78
79
    /**
80
     * @param $key
81
     * @return ParamBag
82
     */
83
    public function getParams(string $key = null): ParamBag
84
    {
85
        return $this->getFactory()->getParams(
86
            $this->getIdentifier($key)
87
        );
88
    }
89
90
    /**
91
     * Get the current identifier.
92
     *
93
     * @return string|null
94
     */
95
    public function getScopeIdentifier()
96
    {
97
        return $this->scopeIdentifier;
98
    }
99
100
    /**
101
     * Get the unique identifier for this scope.
102
     *
103
     * @param string $appendIdentifier
104
     *
105
     * @return string
106
     */
107
    public function getIdentifier(string $appendIdentifier = null): string
108
    {
109
        return implode(
110
            '.',
111
            array_filter(array_merge(
112
                $this->parentScopes,
113
                [
114
                    $this->scopeIdentifier,
115
                    $appendIdentifier
116
                ]
117
            ))
118
        );
119
    }
120
121
    /**
122
     * Getter for parentScopes.
123
     *
124
     * @return array
125
     */
126
    public function getParentScopes(): array
127
    {
128
        return $this->parentScopes;
129
    }
130
131
    /**
132
     * Is Requested.
133
     *
134
     * Check if - in relation to the current scope - this specific segment is allowed.
135
     * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
136
     * scope is a then c is not allowed, even if it is there and potentially transformable.
137
     *
138
     * @internal
139
     *
140
     * @param string $checkScopeSegment
141
     *
142
     * @return bool Returns the new number of elements in the array.
143
     */
144
    public function isRequested($checkScopeSegment): bool
145
    {
146
        return in_array(
147
            $this->_scopeString($checkScopeSegment),
148
            $this->factory->getIncludes()
149
        );
150
    }
151
152
    /**
153
     * Is Excluded.
154
     *
155
     * Check if - in relation to the current scope - this specific segment should
156
     * be excluded. That means, if a.b.c is excluded and the current scope is a.b,
157
     * then c will not be allowed in the transformation whether it appears in
158
     * the list of default or available, requested includes.
159
     *
160
     * @internal
161
     *
162
     * @param string $checkScopeSegment
163
     *
164
     * @return bool
165
     */
166
    public function isExcluded($checkScopeSegment): bool
167
    {
168
        return in_array(
169
            $this->_scopeString($checkScopeSegment),
170
            $this->factory->getExcludes()
171
        );
172
    }
173
174
    /**
175
     * Convert the current data for this scope to an array.
176
     *
177
     * @return array|null
178
     */
179
    public function transform()
180
    {
181
182
        $resource = $this->getResource();
183
        $transformer = $resource->getTransformer();
184
        $data = $resource->getData();
185
186
        $transformedData = [];
187
188
        if ($resource instanceof ItemInterface) {
189
190
            $transformedData = $this->fireTransformer($transformer, $data);
191
192
        } elseif ($resource instanceof CollectionInterface) {
193
194
            foreach ($data as $value) {
195
196
                $transformedData[] = $this->fireTransformer($transformer, $value);
197
198
            }
199
200
        } else {
201
202
            $transformedData = null;
203
204
        }
205
206
        return $transformedData;
207
208
    }
209
210
    /**
211
     * Fire the main transformer.
212
     *
213
     * @internal
214
     *
215
     * @param TransformerInterface|callable $transformer
216
     * @param mixed $data
217
     *
218
     * @return array
219
     */
220
    protected function fireTransformer(callable $transformer, $data): array
221
    {
222
223
        $includedData = [];
224
225
        // Transform data
226
        $transformedData = $this->parseValue($transformer, $data);
227
228
        // Bail now
229
        if (null === $transformedData) {
230
            return $includedData;
231
        }
232
233
        // Conform to array
234
        if (!is_array($transformedData)) {
235
            $transformedData = [$transformedData];
236
        }
237
238
        foreach ($transformedData as $key => $val) {
239
240
            if (!$this->includeValue($transformer, $key)) {
241
                continue;
242
            }
243
244
            $includedData[$key] = $this->parseValue($val, $data, $key);
245
246
        }
247
248
        // Return only the requested fields
249
        $includedData = $this->filterFields($includedData);
250
251
        return $includedData;
252
253
    }
254
255
    /**
256
     * @param callable $transformer
257
     * @param string $key
258
     * @return bool
259
     */
260
    protected function includeValue(callable $transformer, string $key): bool
261
    {
262
263
        // Ignore optional (that have not been explicitly requested)
264
        if ($transformer instanceof TransformerInterface && in_array($key, $transformer->getIncludes(), true) && !$this->isRequested($key)) {
265
            return false;
266
        }
267
268
        // Ignore excludes
269
        if ($this->isExcluded($key)) {
270
            return false;
271
        }
272
273
        return true;
274
275
    }
276
277
    /**
278
     * @param $val
279
     * @param $data
280
     * @param string|null $key
281
     * @return array|string|null
282
     */
283
    protected function parseValue($val, $data, string $key = null)
284
    {
285
286
        // Nesting
287
        if ($val instanceof ResourceInterface) {
288
289
            return $this->getFactory()->transform($val, $key, $this);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 283 can also be of type string; however, flipbox\transform\Factory::transform() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
290
291
        }
292
293
        if (is_callable($val)) {
294
295
            return call_user_func_array($val, [$data, $this, $key]);
296
297
        }
298
299
        return $val;
300
301
    }
302
303
    /**
304
     * Check, if this is the root scope.
305
     *
306
     * @return bool
307
     */
308
    protected function isRootScope(): bool
309
    {
310
        return empty($this->parentScopes);
311
    }
312
313
    /**
314
     * Filter the provided data with the requested filter fields for
315
     * the scope resource
316
     *
317
     * @internal
318
     *
319
     * @param array $data
320
     *
321
     * @return array
322
     */
323
    protected function filterFields(array $data): array
324
    {
325
326
        $fields = $this->getFilterFields();
327
328
        if ($fields === null) {
329
            return $data;
330
        }
331
332
        return array_intersect_key(
333
            $data,
334
            array_flip(
335
                iterator_to_array($fields)
336
            )
337
        );
338
339
    }
340
341
    /**
342
     * Return the requested filter fields for the scope resource
343
     *
344
     * @internal
345
     *
346
     * @return ParamBag|null
347
     */
348
    protected function getFilterFields()
349
    {
350
        return $this->factory->getField(
351
            $this->getScopeIdentifier()
352
        );
353
    }
354
355
    /**
356
     * @param string $checkScopeSegment
357
     * @return string
358
     */
359
    private function _scopeString(string $checkScopeSegment): string
360
    {
361
362
        if ($this->parentScopes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->parentScopes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
363
            $scopeArray = array_slice($this->parentScopes, 1);
364
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
365
        } else {
366
            $scopeArray = [$checkScopeSegment];
367
        }
368
369
        return implode('.', (array)$scopeArray);
370
371
    }
372
373
}
374