Resource   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 393
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 92.42%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 6
dl 0
loc 393
ccs 122
cts 132
cp 0.9242
rs 8.3999
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getResources() 0 4 1
A getId() 0 8 3
A getAttributes() 0 10 1
A filterFields() 0 8 2
A getData() 0 4 1
F toArray() 0 43 10
A isIdentifier() 0 4 2
A toIdentifier() 0 17 3
A getType() 0 4 1
A getOwnFields() 0 8 2
A mergeAttributes() 0 8 2
A getRelationships() 0 6 1
A getUnfilteredRelationships() 0 4 1
A getRelationshipsAsArray() 0 8 1
B buildRelationships() 0 25 5
A mergeRelationships() 0 8 2
A convertRelationshipsToArray() 0 6 1
A merge() 0 4 1
A with() 0 8 1
A fields() 0 6 1
A setData() 0 4 1
A getSerializer() 0 4 1
A setSerializer() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Resource often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Resource, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of JSON-API.
5
 *
6
 * (c) Toby Zerner <[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 Tobscure\JsonApi;
13
14
class Resource implements ElementInterface
15
{
16
    use LinksTrait;
17
    use MetaTrait;
18
19
    /**
20
     * @var mixed
21
     */
22
    protected $data;
23
24
    /**
25
     * @var \Tobscure\JsonApi\SerializerInterface
26
     */
27
    protected $serializer;
28
29
    /**
30
     * A list of relationships to include.
31
     *
32
     * @var array
33
     */
34
    protected $includes = [];
35
36
    /**
37
     * A list of fields to restrict to.
38
     *
39
     * @var array|null
40
     */
41
    protected $fields;
42
43
    /**
44
     * An array of Resources that should be merged into this one.
45
     *
46
     * @var \Tobscure\JsonApi\Resource[]
47
     */
48
    protected $merged = [];
49
50
    /**
51
     * @var \Tobscure\JsonApi\Relationship[]
52
     */
53
    private $relationships;
54
55
    /**
56
     * @param mixed $data
57
     * @param \Tobscure\JsonApi\SerializerInterface $serializer
58
     */
59 51
    public function __construct($data, SerializerInterface $serializer)
60
    {
61 51
        $this->data = $data;
62 51
        $this->serializer = $serializer;
63 51
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 9
    public function getResources()
69
    {
70 9
        return [$this];
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 33
    public function toArray()
77
    {
78 33
        $array = $this->toIdentifier();
79
80 33
        if (! $this->isIdentifier()) {
81 33
            $attributes = $this->getAttributes();
82 33
            if ($attributes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attributes 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...
83 30
                $array['attributes'] = $attributes;
84 30
            }
85 33
        }
86
87 33
        $relationships = $this->getRelationshipsAsArray();
88
89 33
        if (count($relationships)) {
90 12
            $array['relationships'] = $relationships;
91 12
        }
92
93 33
        $links = [];
94 33
        if (! empty($this->links)) {
95 3
            $links = $this->links;
96 3
        }
97 33
        $serializerLinks = $this->serializer->getLinks($this->data);
98 33
        if (! empty($serializerLinks)) {
99 9
            $links = array_merge($serializerLinks, $links);
100 9
        }
101 33
        if (! empty($links)) {
102 9
            $array['links'] = $links;
103 9
        }
104
105 33
        $meta = [];
106 33
        if (! empty($this->meta)) {
107 3
            $meta = $this->meta;
108 3
        }
109 33
        $serializerMeta = $this->serializer->getMeta($this->data);
110 33
        if (! empty($serializerMeta)) {
111 9
            $meta = array_merge($serializerMeta, $meta);
112 9
        }
113 33
        if (! empty($meta)) {
114 9
            $array['meta'] = $meta;
115 9
        }
116
117 33
        return $array;
118
    }
119
120
    /**
121
     * Check whether or not this resource is an identifier (i.e. does it have
122
     * any data attached?).
123
     *
124
     * @return bool
125
     */
126 33
    public function isIdentifier()
127
    {
128 33
        return ! is_object($this->data) && ! is_array($this->data);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 39
    public function toIdentifier()
135
    {
136 39
        if (! $this->data) {
137 3
            return;
138
        }
139
140
        $array = [
141 39
            'type' => $this->getType(),
142 39
            'id' => $this->getId()
143 39
        ];
144
145 39
        if (! empty($this->meta)) {
146 6
            $array['meta'] = $this->meta;
147 6
        }
148
149 39
        return $array;
150
    }
151
152
    /**
153
     * Get the resource type.
154
     *
155
     * @return string
156
     */
157 39
    public function getType()
158
    {
159 39
        return $this->serializer->getType($this->data);
160
    }
161
162
    /**
163
     * Get the resource ID.
164
     *
165
     * @return string
166
     */
167 45
    public function getId()
168
    {
169 45
        if (! is_object($this->data) && ! is_array($this->data)) {
170 6
            return (string) $this->data;
171
        }
172
173 42
        return (string) $this->serializer->getId($this->data);
174
    }
175
176
    /**
177
     * Get the resource attributes.
178
     *
179
     * @return array
180
     */
181 33
    public function getAttributes()
182
    {
183 33
        $attributes = (array) $this->serializer->getAttributes($this->data, $this->getOwnFields());
184
185 33
        $attributes = $this->filterFields($attributes);
186
187 33
        $attributes = $this->mergeAttributes($attributes);
188
189 33
        return $attributes;
190
    }
191
192
    /**
193
     * Get the requested fields for this resource type.
194
     *
195
     * @return array|null
196
     */
197 33
    protected function getOwnFields()
198
    {
199 33
        $type = $this->getType();
200
201 33
        if (isset($this->fields[$type])) {
202 3
            return $this->fields[$type];
203
        }
204 30
    }
205
206
    /**
207
     * Filter the given fields array (attributes or relationships) according
208
     * to the requested fieldset.
209
     *
210
     * @param array $fields
211
     *
212
     * @return array
213
     */
214 33
    protected function filterFields(array $fields)
215
    {
216 33
        if ($requested = $this->getOwnFields()) {
217 3
            $fields = array_intersect_key($fields, array_flip($requested));
218 3
        }
219
220 33
        return $fields;
221
    }
222
223
    /**
224
     * Merge the attributes of merged resources into an array of attributes.
225
     *
226
     * @param array $attributes
227
     *
228
     * @return array
229
     */
230 33
    protected function mergeAttributes(array $attributes)
231
    {
232 33
        foreach ($this->merged as $resource) {
233 3
            $attributes = array_replace_recursive($attributes, $resource->getAttributes());
234 33
        }
235
236 33
        return $attributes;
237
    }
238
239
    /**
240
     * Get the resource relationships.
241
     *
242
     * @return \Tobscure\JsonApi\Relationship[]
243
     */
244 33
    public function getRelationships()
245
    {
246 33
        $relationships = $this->buildRelationships();
247
248 33
        return $this->filterFields($relationships);
249
    }
250
251
    /**
252
     * Get the resource relationships without considering requested ones.
253
     *
254
     * @return \Tobscure\JsonApi\Relationship[]
255
     */
256 9
    public function getUnfilteredRelationships()
257
    {
258 9
        return $this->buildRelationships();
259
    }
260
261
    /**
262
     * Get the resource relationships as an array.
263
     *
264
     * @return array
265
     */
266 33
    public function getRelationshipsAsArray()
267
    {
268 33
        $relationships = $this->getRelationships();
269
270 33
        $relationships = $this->convertRelationshipsToArray($relationships);
271
272 33
        return $this->mergeRelationships($relationships);
273
    }
274
275
    /**
276
     * Get an array of built relationships.
277
     *
278
     * @return \Tobscure\JsonApi\Relationship[]
279
     */
280 33
    protected function buildRelationships()
281
    {
282 33
        if (isset($this->relationships)) {
283 12
            return $this->relationships;
284
        }
285
286 33
        $paths = Util::parseRelationshipPaths($this->includes);
287
288 33
        $relationships = [];
289
290 33
        foreach ($paths as $name => $nested) {
291 12
            $relationship = $this->serializer->getRelationship($this->data, $name);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $relationship is correct as $this->serializer->getRe...hip($this->data, $name) (which targets Tobscure\JsonApi\Seriali...face::getRelationship()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
292
293 12
            if ($relationship) {
294 12
                $relationshipData = $relationship->getData();
295 12
                if ($relationshipData instanceof ElementInterface) {
296 12
                    $relationshipData->with($nested)->fields($this->fields);
297 12
                }
298
299 12
                $relationships[$name] = $relationship;
300 12
            }
301 33
        }
302
303 33
        return $this->relationships = $relationships;
304
    }
305
306
    /**
307
     * Merge the relationships of merged resources into an array of
308
     * relationships.
309
     *
310
     * @param array $relationships
311
     *
312
     * @return array
313
     */
314 33
    protected function mergeRelationships(array $relationships)
315
    {
316 33
        foreach ($this->merged as $resource) {
317 3
            $relationships = array_replace_recursive($relationships, $resource->getRelationshipsAsArray());
318 33
        }
319
320 33
        return $relationships;
321
    }
322
323
    /**
324
     * Convert the given array of Relationship objects into an array.
325
     *
326
     * @param \Tobscure\JsonApi\Relationship[] $relationships
327
     *
328
     * @return array
329
     */
330
    protected function convertRelationshipsToArray(array $relationships)
331
    {
332 33
        return array_map(function (Relationship $relationship) {
333 12
            return $relationship->toArray();
334 33
        }, $relationships);
335
    }
336
337
    /**
338
     * Merge a resource into this one.
339
     *
340
     * @param \Tobscure\JsonApi\Resource $resource
341
     *
342
     * @return void
343
     */
344 3
    public function merge(Resource $resource)
345
    {
346 3
        $this->merged[] = $resource;
347 3
    }
348
349
    /**
350
     * {@inheritdoc}
351
     */
352 12
    public function with($relationships)
353
    {
354 12
        $this->includes = array_unique(array_merge($this->includes, (array) $relationships));
355
356 12
        $this->relationships = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<Tob...\JsonApi\Relationship>> of property $relationships.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
357
358 12
        return $this;
359
    }
360
361
    /**
362
     * {@inheritdoc}
363
     */
364 12
    public function fields($fields)
365
    {
366 12
        $this->fields = $fields;
367
368 12
        return $this;
369
    }
370
371
    /**
372
     * @return mixed
373
     */
374
    public function getData()
375
    {
376
        return $this->data;
377
    }
378
379
    /**
380
     * @param mixed $data
381
     *
382
     * @return void
383
     */
384
    public function setData($data)
385
    {
386
        $this->data = $data;
387
    }
388
389
    /**
390
     * @return \Tobscure\JsonApi\SerializerInterface
391
     */
392
    public function getSerializer()
393
    {
394
        return $this->serializer;
395
    }
396
397
    /**
398
     * @param \Tobscure\JsonApi\SerializerInterface $serializer
399
     *
400
     * @return void
401
     */
402
    public function setSerializer(SerializerInterface $serializer)
403
    {
404
        $this->serializer = $serializer;
405
    }
406
}
407