Test Failed
Pull Request — main (#64)
by Lode
08:15
created

RelationshipObject   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 325
Duplicated Lines 0.92 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 62
lcom 1
cbo 9
dl 3
loc 325
rs 3.44
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A fromAnything() 0 20 5
A fromResource() 0 19 5
A fromCollectionDocument() 0 16 4
A setSelfLink() 0 3 1
A setRelatedLink() 0 3 1
A setPaginationLinks() 0 18 6
A addMeta() 0 7 2
A setResource() 0 7 2
A addResource() 0 7 2
A setMetaObject() 0 3 1
A hasResource() 0 17 6
B isEmpty() 3 19 10
B toArray() 0 24 9
B getNestedContainedResourceObjects() 0 26 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RelationshipObject 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 RelationshipObject, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace alsvanzelf\jsonapi\objects;
4
5
use alsvanzelf\jsonapi\CollectionDocument;
6
use alsvanzelf\jsonapi\exceptions\InputException;
7
use alsvanzelf\jsonapi\helpers\AtMemberManager;
8
use alsvanzelf\jsonapi\helpers\LinksManager;
9
use alsvanzelf\jsonapi\interfaces\ObjectInterface;
10
use alsvanzelf\jsonapi\interfaces\PaginableInterface;
11
use alsvanzelf\jsonapi\interfaces\RecursiveResourceContainerInterface;
12
use alsvanzelf\jsonapi\interfaces\ResourceInterface;
13
use alsvanzelf\jsonapi\objects\LinksObject;
14
use alsvanzelf\jsonapi\objects\MetaObject;
15
use alsvanzelf\jsonapi\objects\ResourceObject;
16
17
class RelationshipObject implements ObjectInterface, PaginableInterface, RecursiveResourceContainerInterface {
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class RelationshipObject
Loading history...
18
	use AtMemberManager, LinksManager;
19
	
20
	const TO_ONE  = 'one';
21
	const TO_MANY = 'many';
22
	
23
	/** @var MetaObject */
24
	protected $meta;
25
	/** @var ResourceInterface */
26
	protected $resource;
27
	/** @var string one of the RelationshipObject::TO_* constants */
28
	protected $type;
29
	/** @var ResourceInterface[] */
30
	protected $resources = [];
31
	
32
	/**
33
	 * @param string $type one of the RelationshipObject::TO_* constants
34
	 * 
35
	 * @throws InputException if $type is unknown
36
	 */
37
	public function __construct($type) {
38
		if (in_array($type, [RelationshipObject::TO_ONE, RelationshipObject::TO_MANY], $strict=true) === false) {
39
			throw new InputException('unknown relationship type "'.$type.'"');
40
		}
41
		
42
		$this->type = $type;
43
	}
44
	
45
	/**
46
	 * human api
47
	 */
48
	
49
	/**
50
	 * create a RelationshipObject from mixed input
51
	 * 
52
	 * @param  mixed  $relation ResourceInterface | ResourceInterface[] | CollectionDocument
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
53
	 * @param  array  $links    optional
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
54
	 * @param  array  $meta     optional
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
55
	 * @return RelationshipObject
56
	 * 
57
	 * @throws InputException if $relation is not one of the supported formats
58
	 */
59
	public static function fromAnything($relation, array $links=[], array $meta=[]) {
60
		if (is_array($relation)) {
61
			$relation = CollectionDocument::fromResources(...$relation);
62
		}
63
		
64
		if ($relation instanceof ResourceInterface) {
65
			$relationshipObject = self::fromResource($relation, $links, $meta);
66
		}
67
		elseif ($relation instanceof CollectionDocument) {
68
			$relationshipObject = self::fromCollectionDocument($relation, $links, $meta);
69
		}
70
		elseif ($relation === null) {
71
			$relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE);
72
		}
73
		else {
74
			throw new InputException('unknown format of relation "'.gettype($relation).'"');
75
		}
76
		
77
		return $relationshipObject;
78
	}
79
	
80
	/**
81
	 * @param  ResourceInterface $resource
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
82
	 * @param  array             $links    optional
83
	 * @param  array             $meta     optional
84
	 * @param  string            $type     optional, one of the RelationshipObject::TO_* constants, defaults to RelationshipObject::TO_ONE
85
	 * @return RelationshipObject
86
	 */
87
	public static function fromResource(ResourceInterface $resource, array $links=[], array $meta=[], $type=RelationshipObject::TO_ONE) {
88
		$relationshipObject = new self($type);
89
		
90
		if ($type === RelationshipObject::TO_ONE) {
91
			$relationshipObject->setResource($resource);
92
		}
93
		elseif ($type === RelationshipObject::TO_MANY) {
94
			$relationshipObject->addResource($resource);
95
		}
96
		
97
		if ($links !== []) {
98
			$relationshipObject->setLinksObject(LinksObject::fromArray($links));
99
		}
100
		if ($meta !== []) {
101
			$relationshipObject->setMetaObject(MetaObject::fromArray($meta));
102
		}
103
		
104
		return $relationshipObject;
105
	}
106
	
107
	/**
108
	 * @param  CollectionDocument $collectionDocument
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
109
	 * @param  array              $links              optional
110
	 * @param  array              $meta               optional
111
	 * @return RelationshipObject
112
	 */
113
	public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]) {
114
		$relationshipObject = new self(RelationshipObject::TO_MANY);
115
		
116
		foreach ($collectionDocument->getContainedResources() as $resource) {
117
			$relationshipObject->addResource($resource);
118
		}
119
		
120
		if ($links !== []) {
121
			$relationshipObject->setLinksObject(LinksObject::fromArray($links));
122
		}
123
		if ($meta !== []) {
124
			$relationshipObject->setMetaObject(MetaObject::fromArray($meta));
125
		}
126
		
127
		return $relationshipObject;
128
	}
129
	
130
	/**
131
	 * @param string $href
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
132
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
133
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
134
	public function setSelfLink($href, array $meta=[]) {
135
		$this->addLink('self', $href, $meta);
136
	}
137
	
138
	/**
139
	 * @param string $href
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
140
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
141
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
142
	public function setRelatedLink($href, array $meta=[]) {
143
		$this->addLink('related', $href, $meta);
144
	}
145
	
146
	/**
0 ignored issues
show
Coding Style introduced by
Parameter $lastHref should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $firstHref should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $nextHref should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $previousHref should have a doc-comment as per coding-style.
Loading history...
147
	 * @inheritDoc
148
	 * 
149
	 * @throws InputException if used on a to-one relationship
150
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
151
	public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) {
152
		if ($this->type === RelationshipObject::TO_ONE) {
153
			throw new InputException('can not add pagination links to a to-one relationship');
154
		}
155
		
156
		if ($previousHref !== null) {
157
			$this->addLink('prev', $previousHref);
158
		}
159
		if ($nextHref !== null) {
160
			$this->addLink('next', $nextHref);
161
		}
162
		if ($firstHref !== null) {
163
			$this->addLink('first', $firstHref);
164
		}
165
		if ($lastHref !== null) {
166
			$this->addLink('last', $lastHref);
167
		}
168
	}
169
	
170
	/**
171
	 * @param string $key
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
172
	 * @param mixed  $value
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
173
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
174
	public function addMeta($key, $value) {
175
		if ($this->meta === null) {
176
			$this->setMetaObject(new MetaObject());
177
		}
178
		
179
		$this->meta->add($key, $value);
180
	}
181
	
182
	/**
183
	 * spec api
184
	 */
185
	
186
	/**
187
	 * set the resource on a to-one relationship
188
	 * 
189
	 * @param ResourceInterface $resource
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
190
	 * 
191
	 * @throws InputException if used on a to-many relationship, use {@see ->addResource()} instead
192
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
193
	public function setResource(ResourceInterface $resource) {
194
		if ($this->type === RelationshipObject::TO_MANY) {
195
			throw new InputException('can not set a resource on a to-many relationship, use ->addResource()');
196
		}
197
		
198
		$this->resource = $resource;
199
	}
200
	
201
	/**
202
	 * add a resource to a to-many relationship
203
	 * 
204
	 * @param ResourceInterface $resource
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
205
	 * 
206
	 * @throws InputException if used on a to-one relationship, use {@see ->setResource()} instead
207
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
208
	public function addResource(ResourceInterface $resource) {
209
		if ($this->type === RelationshipObject::TO_ONE) {
210
			throw new InputException('can not add a resource to a to-one relationship, use ->setResource()');
211
		}
212
		
213
		$this->resources[] = $resource;
214
	}
215
	
216
	/**
217
	 * @param MetaObject $metaObject
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
218
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
219
	public function setMetaObject(MetaObject $metaObject) {
220
		$this->meta = $metaObject;
221
	}
222
	
223
	/**
224
	 * internal api
225
	 */
226
	
227
	/**
228
	 * whether or not the $otherResource is (one of) the resource(s) inside the relationship
229
	 * 
230
	 * @internal
231
	 * 
232
	 * @param  ResourceInterface $otherResource
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
233
	 * @return boolean
234
	 */
235
	public function hasResource(ResourceInterface $otherResource) {
236
		if ($this->isEmpty()) {
237
			return false;
238
		}
239
		if ($this->type === RelationshipObject::TO_ONE) {
240
			return $this->resource->getResource()->equals($otherResource->getResource());
241
		}
242
		if ($this->type === RelationshipObject::TO_MANY) {
243
			foreach ($this->resources as $ownResource) {
244
				if ($ownResource->getResource()->equals($otherResource->getResource())) {
245
					return true;
246
				}
247
			}
248
		}
249
		
250
		return false;
251
	}
252
	
253
	/**
254
	 * ObjectInterface
255
	 */
256
	
257
	/**
258
	 * @inheritDoc
259
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
260
	public function isEmpty() {
261
		if ($this->type === RelationshipObject::TO_ONE && $this->resource !== null) {
262
			return false;
263
		}
264
		if ($this->type === RelationshipObject::TO_MANY && $this->resources !== []) {
265
			return false;
266
		}
267 View Code Duplication
		if ($this->links !== null && $this->links->isEmpty() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
268
			return false;
269
		}
270
		if ($this->meta !== null && $this->meta->isEmpty() === false) {
271
			return false;
272
		}
273
		if ($this->hasAtMembers()) {
274
			return false;
275
		}
276
		
277
		return true;
278
	}
279
	
280
	/**
281
	 * @inheritDoc
282
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
283
	public function toArray() {
284
		$array = $this->getAtMembers();
285
		
286
		if ($this->links !== null && $this->links->isEmpty() === false) {
287
			$array['links'] = $this->links->toArray();
288
		}
289
		if ($this->type === RelationshipObject::TO_ONE) {
290
			$array['data'] = null;
291
			if ($this->resource !== null) {
292
				$array['data'] = $this->resource->getResource($identifierOnly=true)->toArray();
293
			}
294
		}
295
		if ($this->type === RelationshipObject::TO_MANY) {
296
			$array['data'] = [];
297
			foreach ($this->resources as $resource) {
298
				$array['data'][] = $resource->getResource($identifierOnly=true)->toArray();
299
			}
300
		}
301
		if ($this->meta !== null && $this->meta->isEmpty() === false) {
302
			$array['meta'] = $this->meta->toArray();
303
		}
304
		
305
		return $array;
306
	}
307
	
308
	/**
309
	 * RecursiveResourceContainerInterface
310
	 */
311
	
312
	/**
313
	 * @inheritDoc
314
	 */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
315
	public function getNestedContainedResourceObjects() {
316
		if ($this->isEmpty()) {
317
			return [];
318
		}
319
		
320
		$resources       = ($this->type === RelationshipObject::TO_ONE) ? [$this->resource] : $this->resources;
321
		$resourceObjects = [];
322
		
323
		foreach ($resources as $resource) {
324
			if ($resource->getResource() instanceof ResourceObject === false) {
325
				continue;
326
			}
327
			
328
			/** @var ResourceObject */
329
			$resourceObject = $resource->getResource();
330
			
331
			if ($resourceObject->hasIdentifierPropertiesOnly()) {
332
				continue;
333
			}
334
			
335
			$resourceObjects[] = $resourceObject;
336
			$resourceObjects   = array_merge($resourceObjects, $resourceObject->getNestedContainedResourceObjects());
337
		}
338
		
339
		return $resourceObjects;
340
	}
341
}
342