Completed
Push — master ( 11e5ad...663d1c )
by Toby
02:33
created

Resource   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 363
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 91.8%

Importance

Changes 11
Bugs 1 Features 4
Metric Value
wmc 42
c 11
b 1
f 4
lcom 1
cbo 6
dl 0
loc 363
ccs 112
cts 122
cp 0.918
rs 8.295

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getResources() 0 4 1
D toArray() 0 38 8
A isIdentifier() 0 4 2
A toIdentifier() 0 13 2
A getType() 0 4 1
A getId() 0 8 3
A getAttributes() 0 10 1
A getOwnFields() 0 8 2
A filterFields() 0 8 2
A mergeAttributes() 0 8 2
A getRelationships() 0 6 1
A getUnfilteredRelationships() 0 4 1
A getRelationshipsAsArray() 0 8 1
A buildRelationships() 0 21 4
A mergeRelationships() 0 8 2
A convertRelationshipsToArray() 0 6 1
A merge() 0 4 1
A with() 0 6 1
A fields() 0 6 1
A getData() 0 4 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 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 Resource[]
47
     */
48
    protected $merged = [];
49
50
    /**
51
     * @param mixed $data
52
     * @param SerializerInterface $serializer
53
     */
54 33
    public function __construct($data, SerializerInterface $serializer)
55
    {
56 33
        $this->data = $data;
57 33
        $this->serializer = $serializer;
58 33
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 3
    public function getResources()
64
    {
65 3
        return [$this];
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 21
    public function toArray()
72
    {
73 21
        $array = $this->toIdentifier();
74
75 21
        $array['attributes'] = $this->getAttributes();
76
77 21
        $relationships = $this->getRelationshipsAsArray();
78
79 21
        if (count($relationships)) {
80 3
            $array['relationships'] = $relationships;
81 3
        }
82
83 21
        $links = [];
84 21
        if (! empty($this->links)) {
85 3
            $links = $this->links;
86 3
        }
87 21
        $serializerLinks = $this->serializer->getLinks($this->data);
88 21
        if (! empty($serializerLinks)) {
89 9
            $links = array_merge($serializerLinks, $links);
90 9
        }
91 21
        if (! empty($links)) {
92 9
            $array['links'] = $links;
93 9
        }
94
95 21
        $meta = [];
96 21
        if (! empty($this->meta)) {
97 3
            $meta = $this->meta;
98 3
        }
99 21
        $serializerMeta = $this->serializer->getMeta($this->data);
100 21
        if (! empty($serializerMeta)) {
101 9
            $meta = array_merge($serializerMeta, $meta);
102 9
        }
103 21
        if (! empty($meta)) {
104 9
            $array['meta'] = $meta;
105 9
        }
106
107 21
        return $array;
108
    }
109
110
    /**
111
     * Check whether or not this resource is an identifier (i.e. does it have
112
     * any data attached?).
113
     *
114
     * @return bool
115
     */
116 3
    public function isIdentifier()
117
    {
118 3
        return ! is_object($this->data) && ! is_array($this->data);
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 27
    public function toIdentifier()
125
    {
126
        $array = [
127 27
            'type' => $this->getType(),
128 27
            'id' => $this->getId()
129 27
        ];
130
131 27
        if (! empty($this->meta)) {
132 6
            $array['meta'] = $this->meta;
133 6
        }
134
135 27
        return $array;
136
    }
137
138
    /**
139
     * Get the resource type.
140
     *
141
     * @return string
142
     */
143 27
    public function getType()
144
    {
145 27
        return $this->serializer->getType($this->data);
146
    }
147
148
    /**
149
     * Get the resource ID.
150
     *
151
     * @return string
152
     */
153 33
    public function getId()
154
    {
155 33
        if (! is_object($this->data) && ! is_array($this->data)) {
156 6
            return (string) $this->data;
157
        }
158
159 30
        return (string) $this->serializer->getId($this->data);
160
    }
161
162
    /**
163
     * Get the resource attributes.
164
     *
165
     * @return array
166
     */
167 21
    public function getAttributes()
168
    {
169 21
        $attributes = (array) $this->serializer->getAttributes($this->data, $this->getOwnFields());
170
171 21
        $attributes = $this->filterFields($attributes);
172
173 21
        $attributes = $this->mergeAttributes($attributes);
174
175 21
        return $attributes;
176
    }
177
178
    /**
179
     * Get the requested fields for this resource type.
180
     *
181
     * @return array|null
182
     */
183 21
    protected function getOwnFields()
184
    {
185 21
        $type = $this->getType();
186
187 21
        if (isset($this->fields[$type])) {
188 3
            return $this->fields[$type];
189
        }
190 18
    }
191
192
    /**
193
     * Filter the given fields array (attributes or relationships) according
194
     * to the requested fieldset.
195
     *
196
     * @param array $fields
197
     * @return array
198
     */
199 21
    protected function filterFields(array $fields)
200
    {
201 21
        if ($requested = $this->getOwnFields()) {
202 3
            $fields = array_intersect_key($fields, array_flip($requested));
203 3
        }
204
205 21
        return $fields;
206
    }
207
208
    /**
209
     * Merge the attributes of merged resources into an array of attributes.
210
     *
211
     * @param array $attributes
212
     * @return array
213
     */
214 21
    protected function mergeAttributes(array $attributes)
215
    {
216 21
        foreach ($this->merged as $resource) {
217 3
            $attributes = array_replace_recursive($attributes, $resource->getAttributes());
0 ignored issues
show
Bug introduced by
The method getAttributes cannot be called on $resource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
218 21
        }
219
220 21
        return $attributes;
221
    }
222
223
    /**
224
     * Get the resource relationships.
225
     *
226
     * @return Relationship[]
227
     */
228 21
    public function getRelationships()
229
    {
230 21
        $relationships = $this->buildRelationships();
231
232 21
        return $this->filterFields($relationships);
233
    }
234
235
    /**
236
     * Get the resource relationships without considering requested ones.
237
     *
238
     * @return Relationship[]
239
     */
240 3
    public function getUnfilteredRelationships()
241
    {
242 3
        return $this->buildRelationships();
243
    }
244
245
    /**
246
     * Get the resource relationships as an array.
247
     *
248
     * @return array
249
     */
250 21
    public function getRelationshipsAsArray()
251
    {
252 21
        $relationships = $this->getRelationships();
253
254 21
        $relationships = $this->convertRelationshipsToArray($relationships);
255
256 21
        return $this->mergeRelationships($relationships);
257
    }
258
259
    /**
260
     * Get an array of built relationships.
261
     *
262
     * @return Relationship[]
263
     */
264 21
    protected function buildRelationships()
265
    {
266 21
        $paths = Util::parseRelationshipPaths($this->includes);
267
268 21
        $relationships = [];
269
270 21
        foreach ($paths as $name => $nested) {
271 3
            $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...
272
273 3
            if ($relationship) {
274 3
                $relationshipData = $relationship->getData();
275 3
                if ($relationshipData instanceof ElementInterface) {
276 3
                    $relationshipData->with($nested)->fields($this->fields);
277 3
                }
278
279 3
                $relationships[$name] = $relationship;
280 3
            }
281 21
        }
282
283 21
        return $relationships;
284
    }
285
286
    /**
287
     * Merge the relationships of merged resources into an array of
288
     * relationships.
289
     *
290
     * @param array $relationships
291
     * @return array
292
     */
293 21
    protected function mergeRelationships(array $relationships)
294
    {
295 21
        foreach ($this->merged as $resource) {
296 3
            $relationships = array_replace_recursive($relationships, $resource->getRelationshipsAsArray());
0 ignored issues
show
Bug introduced by
The method getRelationshipsAsArray cannot be called on $resource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
297 21
        }
298
299 21
        return $relationships;
300
    }
301
302
    /**
303
     * Convert the given array of Relationship objects into an array.
304
     *
305
     * @param Relationship[] $relationships
306
     * @return array
307
     */
308
    protected function convertRelationshipsToArray(array $relationships)
309
    {
310 21
        return array_map(function (Relationship $relationship) {
311 3
            return $relationship->toArray();
312 21
        }, $relationships);
313
    }
314
315
    /**
316
     * Merge a resource into this one.
317
     *
318
     * @param Resource $resource
319
     */
320 3
    public function merge(Resource $resource)
321
    {
322 3
        $this->merged[] = $resource;
323 3
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328 3
    public function with($relationships)
329
    {
330 3
        $this->includes = array_unique(array_merge($this->includes, (array) $relationships));
331
332 3
        return $this;
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338 6
    public function fields($fields)
339
    {
340 6
        $this->fields = $fields;
341
342 6
        return $this;
343
    }
344
345
    /**
346
     * @return mixed
347
     */
348
    public function getData()
349
    {
350
        return $this->data;
351
    }
352
353
    /**
354
     * @param mixed $data
355
     */
356
    public function setData($data)
357
    {
358
        $this->data = $data;
359
    }
360
361
    /**
362
     * @return SerializerInterface
363
     */
364
    public function getSerializer()
365
    {
366
        return $this->serializer;
367
    }
368
369
    /**
370
     * @param SerializerInterface $serializer
371
     */
372
    public function setSerializer(SerializerInterface $serializer)
373
    {
374
        $this->serializer = $serializer;
375
    }
376
}
377