Completed
Push — master ( 57919c...74fef5 )
by Nate
03:31
created

Scope::fireTransformer()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 37
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 13
nc 5
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\Collection;
14
use flipbox\transform\resources\Item;
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 Item) {
189
190
            $transformedData = $this->fireTransformer($transformer, $data);
191
192
        } elseif ($resource instanceof Collection) {
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 = call_user_func_array($transformer, [$data, $this, null]);
227
228
        foreach ($transformedData as $key => $val) {
229
230
            // Ignore optional (that have not been explicitly requested)
231
            if ($transformer instanceof TransformerInterface && in_array($key, $transformer->getIncludes(), true) && !$this->isRequested($key)) {
232
                continue;
233
            }
234
235
            // Ignore excludes
236
            if ($this->isExcluded($key)) {
237
                continue;
238
            }
239
240
            // Nested transformer
241
            if (is_callable($val)) {
242
243
                $val = call_user_func_array($val, [$data, $this, $key]);
244
245
            }
246
247
            $includedData[$key] = $val;
248
249
        }
250
251
        // Stick only with requested fields
252
        $includedData = $this->filterFields($includedData);
253
254
        return $includedData;
255
256
    }
257
258
    /**
259
     * Check, if this is the root scope.
260
     *
261
     * @return bool
262
     */
263
    protected function isRootScope(): bool
264
    {
265
        return empty($this->parentScopes);
266
    }
267
268
    /**
269
     * Filter the provided data with the requested filter fields for
270
     * the scope resource
271
     *
272
     * @internal
273
     *
274
     * @param array $data
275
     *
276
     * @return array
277
     */
278
    protected function filterFields(array $data): array
279
    {
280
281
        $fields = $this->getFilterFields();
282
283
        if ($fields === null) {
284
            return $data;
285
        }
286
287
        return array_intersect_key(
288
            $data,
289
            array_flip(
290
                iterator_to_array($fields)
291
            )
292
        );
293
294
    }
295
296
    /**
297
     * Return the requested filter fields for the scope resource
298
     *
299
     * @internal
300
     *
301
     * @return ParamBag|null
302
     */
303
    protected function getFilterFields()
304
    {
305
        return $this->factory->getField(
306
            $this->getScopeIdentifier()
307
        );
308
    }
309
310
    /**
311
     * @param string $checkScopeSegment
312
     * @return string
313
     */
314
    private function _scopeString(string $checkScopeSegment): string
315
    {
316
317
        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...
318
            $scopeArray = array_slice($this->parentScopes, 1);
319
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
320
        } else {
321
            $scopeArray = [$checkScopeSegment];
322
        }
323
324
        return implode('.', (array)$scopeArray);
325
326
    }
327
328
}
329