Completed
Push — master ( 9beb61...fcf0cf )
by Jodie
03:20
created

Scope::transform()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.0218

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 8
cts 9
cp 0.8889
rs 9.552
c 0
b 0
f 0
cc 4
nc 4
nop 1
crap 4.0218
1
<?php
2
3
namespace Rexlabs\Smokescreen\Transformer;
4
5
use Rexlabs\Smokescreen\Exception\InvalidTransformerException;
6
use Rexlabs\Smokescreen\Includes\Includes;
7
use Rexlabs\Smokescreen\Resource\ResourceInterface;
8
9
class Scope
10
{
11
    /** @var mixed|ResourceInterface  */
12
    protected $resource;
13
14
    /** @var Includes */
15
    protected $includes;
16
17
    /** @var Scope|null */
18
    protected $parent;
19
20
    /**
21
     * Scope constructor.
22
     *
23
     * @param ResourceInterface|mixed $resource
24
     * @param Includes                $includes
25
     * @param Scope|null              $parent
26
     */
27 16
    public function __construct($resource, Includes $includes, Scope $parent = null)
28
    {
29 16
        $this->resource = $resource;
30 16
        $this->includes = $includes;
31 16
        $this->parent = $parent;
32 16
    }
33
34
    /**
35
     * @return TransformerInterface|mixed|null
36
     */
37 14
    public function transformer()
38
    {
39 14
       return $this->resource instanceof ResourceInterface ?
40 14
           $this->resource->getTransformer() : null;
41
    }
42
43
    /**
44
     * @return Includes
45
     */
46 14
    public function includes(): Includes
47
    {
48 14
        return $this->includes;
49
    }
50
51
    /**
52
     * @param Includes $includes
53
     */
54
    public function setIncludes(Includes $includes)
55
    {
56
        $this->includes = $includes;
57
    }
58
59
    /**
60
     * @return mixed|ResourceInterface
61
     */
62 16
    public function resource()
63
    {
64 16
        return $this->resource;
65
    }
66
67
    /**
68
     * @param mixed|ResourceInterface $resource
69
     */
70
    public function setResource($resource)
71
    {
72
        $this->resource = $resource;
73
    }
74
75 11
    public function defaultIncludeKeys(): array
76
    {
77 11
        $defaultIncludeKeys = [];
78 11
        if (($transformer = $this->transformer()) !== null && ($transformer instanceof  TransformerInterface)) {
79 2
            $defaultIncludeKeys = $transformer->getDefaultIncludes();
80
        }
81
82 11
        return $defaultIncludeKeys;
83
    }
84
85
    /**
86
     * List of array keys identifying the available includes for this resource.
87
     * @return array
88
     */
89 14
    public function availableIncludeKeys(): array
90
    {
91 14
        $availableKeys = [];
92 14
        if (($transformer = $this->transformer()) !== null && ($transformer instanceof  TransformerInterface)) {
93 7
            $availableKeys = $transformer->getAvailableIncludes();
94
        }
95
96 14
        return $availableKeys;
97
    }
98
99
    /**
100
     * The include keys that were requested.
101
     * @return array
102
     */
103 14
    public function requestedIncludeKeys(): array
104
    {
105 14
        return $this->includes()->baseKeys();
106
    }
107
108
    /**
109
     * The include keys that were either requested or (if empty) the ones
110
     * that are are enabled by default.
111
     * @return array
112
     */
113 14
    public function includeKeys(): array
114
    {
115
        // Wanted includes is a either the explicit includes requested, or the defaults for the transformer.
116 14
        return $this->requestedIncludeKeys() ?: $this->defaultIncludeKeys();
117
    }
118
119 14
    public function includeMap(): array
120
    {
121 14
        $map = [];
122 14
        if (($transformer = $this->transformer()) !== null && ($transformer instanceof  TransformerInterface)) {
123 7
            $map = $transformer->getIncludeMap();
124
        }
125 14
        return $map;
126
    }
127
128
    /**
129
     * @param $key
130
     *
131
     * @return array
132
     */
133
    public function includeDefinitionFor($key): array
134
    {
135
        return $this->includeMap()[$key] ?? null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->includeMap()[$key] ?? null could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
136
    }
137
138
    /**
139
     * A list of include keys that were requested and are available for
140
     * usage (eg. they are declared in the transformer).
141
     * @return array
142
     */
143 14
    public function resolvedIncludeKeys(): array
144
    {
145 14
        $availableIncludeKeys = $this->availableIncludeKeys();
146 14
        return array_filter($this->includeKeys(), function ($includeKey) use ($availableIncludeKeys) {
147 7
            return \in_array($includeKey, $availableIncludeKeys, true);
148 14
        });
149
    }
150
151
    /**
152
     * Get a list of relationship keys for all of the includes which
153
     * have been resolved.
154
     * @return array
155
     */
156 14
    public function resolvedRelationshipKeys(): array
157
    {
158 14
        $includeMap = $this->includeMap();
159
160 14
        $keys = [];
161 14
        foreach ($this->resolvedIncludeKeys() as $includeKey) {
162 6
            $relations = $includeMap[$includeKey]['relation'] ?? [];
163 6
            if (\count($relations) > 0) {
164 6
                array_push($keys, ...$relations);
165
            }
166
        }
167 14
        return array_unique($keys);
168
    }
169
170 7
    public function filterProps(): array
171
    {
172
        // We can consider our props anything that has not been mapped.
173 7
        $resolvedIncludeKeys = $this->resolvedIncludeKeys();
174 7
        $keys = array_filter($this->includeKeys(), function ($includeKey) use ($resolvedIncludeKeys) {
175 7
            return !\in_array($includeKey, $resolvedIncludeKeys, true);
176 7
        });
177
178
        // Were any filter props explicitly provided?
179
        // If not, see if defaults were provided from the transformer.
180 7
        if (empty($keys) && ($transformer = $this->transformer()) !== null) {
181
            // No explicit props provided
182 7
            $defaultProps = $transformer->getDefaultProps();
183 7
            if (!empty($defaultProps)) {
184 1
                $keys = $defaultProps;
185
            }
186
        }
187
188 7
        return $keys;
189
    }
190
191
    /**
192
     * @param mixed $data
193
     *
194
     * @return array
195
     */
196 14
    public function transform($data)
197
    {
198 14
        $transformer = $this->transformer();
199
200
        // Handle when no transformer is present
201 14
        if (empty($transformer)) {
202
            // No transformation
203 7
            return $data;
204
        }
205
206
        // Handle when transformer is a callable
207 8
        if (\is_callable($transformer)) {
208
            // Simply run callable on the data and return the result
209 3
            return $transformer($data);
210
        }
211
212
        // Ensure we're working with a real transformer from this point forward.
213 7
        if (!($transformer instanceof TransformerInterface)) {
214
            throw new InvalidTransformerException('Expected a valid transformer');
215
        }
216
217
        // Let the transformer do it's thing!
218 7
        return $this->filterData($transformer->getTransformedData($data));
219
    }
220
221
    /**
222
     * @return null|Scope
223
     */
224
    public function parent()
225
    {
226
        return $this->parent;
227
    }
228
229
    /**
230
     * Filters
231
     * @param $data
232
     *
233
     * @return array
234
     */
235 7
    protected function filterData($data)
236
    {
237
        // Filter the sparse field-set if we have a specific list of properties
238
        // defined that we want.
239 7
        $filterProps = $this->filterProps();
240 7
        if (!empty($filterProps)) {
241 1
            $filteredData = array_filter($data, function ($key) use ($filterProps) {
242 1
                return \in_array($key, $filterProps, true);
243 1
            }, ARRAY_FILTER_USE_KEY);
244
245
            // We must always have some data after filtering, so if our filtered
246
            // data is empty, we should just ignore it ...
247 1
            if (!empty($filteredData)) {
248 1
                $data = $filteredData;
249
            }
250
        }
251
252 7
        return $data;
253
    }
254
}