RelationshipObject::fromResource()   A
last analyzed

Complexity

Conditions 5
Paths 12

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 12
nop 4
dl 0
loc 18
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
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\ExtensionMemberManager;
9
use alsvanzelf\jsonapi\helpers\LinksManager;
10
use alsvanzelf\jsonapi\interfaces\ObjectInterface;
11
use alsvanzelf\jsonapi\interfaces\PaginableInterface;
12
use alsvanzelf\jsonapi\interfaces\RecursiveResourceContainerInterface;
13
use alsvanzelf\jsonapi\interfaces\ResourceInterface;
14
use alsvanzelf\jsonapi\objects\LinksObject;
15
use alsvanzelf\jsonapi\objects\MetaObject;
16
use alsvanzelf\jsonapi\objects\ResourceObject;
17
18
class RelationshipObject implements ObjectInterface, PaginableInterface, RecursiveResourceContainerInterface {
19
	use AtMemberManager, ExtensionMemberManager, LinksManager;
20
	
21
	const TO_ONE  = 'one';
22
	const TO_MANY = 'many';
23
	
24
	/** @var MetaObject */
25
	protected $meta;
26
	/** @var ResourceInterface */
27
	protected $resource;
28
	/** @var string one of the RelationshipObject::TO_* constants */
29
	protected $type;
30
	/** @var ResourceInterface[] */
31
	protected $resources = [];
32
	
33
	/**
34
	 * @param string $type one of the RelationshipObject::TO_* constants
35
	 * 
36
	 * @throws InputException if $type is unknown
37
	 */
38 60
	public function __construct($type) {
39 60
		if (in_array($type, [RelationshipObject::TO_ONE, RelationshipObject::TO_MANY], $strict=true) === false) {
40 1
			throw new InputException('unknown relationship type "'.$type.'"');
41
		}
42
		
43 59
		$this->type = $type;
44
	}
45
	
46
	/**
47
	 * human api
48
	 */
49
	
50
	/**
51
	 * create a RelationshipObject from mixed input
52
	 * 
53
	 * @param  mixed  $relation ResourceInterface | ResourceInterface[] | CollectionDocument
54
	 * @param  array  $links    optional
55
	 * @param  array  $meta     optional
56
	 * @return RelationshipObject
57
	 * 
58
	 * @throws InputException if $relation is not one of the supported formats
59
	 */
60 32
	public static function fromAnything($relation, array $links=[], array $meta=[]) {
61 32
		if (is_array($relation)) {
62 3
			$relation = CollectionDocument::fromResources(...$relation);
63
		}
64
		
65 32
		if ($relation instanceof ResourceInterface) {
66 27
			$relationshipObject = self::fromResource($relation, $links, $meta);
67
		}
68 7
		elseif ($relation instanceof CollectionDocument) {
69 5
			$relationshipObject = self::fromCollectionDocument($relation, $links, $meta);
70
		}
71 2
		elseif ($relation === null) {
72 1
			$relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE);
73
		}
74
		else {
75 1
			throw new InputException('unknown format of relation "'.gettype($relation).'"');
76
		}
77
		
78 31
		return $relationshipObject;
79
	}
80
	
81
	/**
82
	 * @param  ResourceInterface $resource
83
	 * @param  array             $links    optional
84
	 * @param  array             $meta     optional
85
	 * @param  string            $type     optional, one of the RelationshipObject::TO_* constants, defaults to RelationshipObject::TO_ONE
86
	 * @return RelationshipObject
87
	 */
88 30
	public static function fromResource(ResourceInterface $resource, array $links=[], array $meta=[], $type=RelationshipObject::TO_ONE) {
89 30
		$relationshipObject = new self($type);
90
		
91 30
		if ($type === RelationshipObject::TO_ONE) {
92 29
			$relationshipObject->setResource($resource);
93
		}
94 1
		elseif ($type === RelationshipObject::TO_MANY) {
95 1
			$relationshipObject->addResource($resource);
96
		}
97
		
98 30
		if ($links !== []) {
99 2
			$relationshipObject->setLinksObject(LinksObject::fromArray($links));
100
		}
101 30
		if ($meta !== []) {
102 1
			$relationshipObject->setMetaObject(MetaObject::fromArray($meta));
103
		}
104
		
105 30
		return $relationshipObject;
106
	}
107
	
108
	/**
109
	 * @param  CollectionDocument $collectionDocument
110
	 * @param  array              $links              optional
111
	 * @param  array              $meta               optional
112
	 * @return RelationshipObject
113
	 */
114 6
	public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]) {
115 6
		$relationshipObject = new self(RelationshipObject::TO_MANY);
116
		
117 6
		foreach ($collectionDocument->getContainedResources() as $resource) {
118 6
			$relationshipObject->addResource($resource);
119
		}
120
		
121 6
		if ($links !== []) {
122 1
			$relationshipObject->setLinksObject(LinksObject::fromArray($links));
123
		}
124 6
		if ($meta !== []) {
125 1
			$relationshipObject->setMetaObject(MetaObject::fromArray($meta));
126
		}
127
		
128 6
		return $relationshipObject;
129
	}
130
	
131
	/**
132
	 * @param string $href
133
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
134
	 */
135 1
	public function setSelfLink($href, array $meta=[]) {
136 1
		$this->addLink('self', $href, $meta);
137
	}
138
	
139
	/**
140
	 * @param string $href
141
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
142
	 */
143 1
	public function setRelatedLink($href, array $meta=[]) {
144 1
		$this->addLink('related', $href, $meta);
145
	}
146
	
147
	/**
148
	 * @inheritDoc
149
	 * 
150
	 * @throws InputException if used on a to-one relationship
151
	 */
152 2
	public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) {
153 2
		if ($this->type === RelationshipObject::TO_ONE) {
154 1
			throw new InputException('can not add pagination links to a to-one relationship');
155
		}
156
		
157 1
		if ($previousHref !== null) {
158 1
			$this->addLink('prev', $previousHref);
159
		}
160 1
		if ($nextHref !== null) {
161 1
			$this->addLink('next', $nextHref);
162
		}
163 1
		if ($firstHref !== null) {
164 1
			$this->addLink('first', $firstHref);
165
		}
166 1
		if ($lastHref !== null) {
167 1
			$this->addLink('last', $lastHref);
168
		}
169
	}
170
	
171
	/**
172
	 * @param string $key
173
	 * @param mixed  $value
174
	 */
175 2
	public function addMeta($key, $value) {
176 2
		if ($this->meta === null) {
177 2
			$this->setMetaObject(new MetaObject());
178
		}
179
		
180 2
		$this->meta->add($key, $value);
181
	}
182
	
183
	/**
184
	 * spec api
185
	 */
186
	
187
	/**
188
	 * set the resource on a to-one relationship
189
	 * 
190
	 * @param ResourceInterface $resource
191
	 * 
192
	 * @throws InputException if used on a to-many relationship, use {@see ->addResource()} instead
193
	 */
194 37
	public function setResource(ResourceInterface $resource) {
195 37
		if ($this->type === RelationshipObject::TO_MANY) {
196 1
			throw new InputException('can not set a resource on a to-many relationship, use ->addResource()');
197
		}
198
		
199 36
		$this->resource = $resource;
200
	}
201
	
202
	/**
203
	 * add a resource to a to-many relationship
204
	 * 
205
	 * @param ResourceInterface $resource
206
	 * 
207
	 * @throws InputException if used on a to-one relationship, use {@see ->setResource()} instead
208
	 */
209 12
	public function addResource(ResourceInterface $resource) {
210 12
		if ($this->type === RelationshipObject::TO_ONE) {
211 1
			throw new InputException('can not add a resource to a to-one relationship, use ->setResource()');
212
		}
213
		
214 11
		$this->resources[] = $resource;
215
	}
216
	
217
	/**
218
	 * @param MetaObject $metaObject
219
	 */
220 6
	public function setMetaObject(MetaObject $metaObject) {
221 6
		$this->meta = $metaObject;
222
	}
223
	
224
	/**
225
	 * internal api
226
	 */
227
	
228
	/**
229
	 * whether or not the $otherResource is (one of) the resource(s) inside the relationship
230
	 * 
231
	 * @internal
232
	 * 
233
	 * @param  ResourceInterface $otherResource
234
	 * @return boolean
235
	 */
236 21
	public function hasResource(ResourceInterface $otherResource) {
237 21
		if ($this->isEmpty()) {
238 1
			return false;
239
		}
240 20
		if ($this->type === RelationshipObject::TO_ONE) {
241 18
			return $this->resource->getResource()->equals($otherResource->getResource());
242
		}
243 4
		if ($this->type === RelationshipObject::TO_MANY) {
244 4
			foreach ($this->resources as $ownResource) {
245 4
				if ($ownResource->getResource()->equals($otherResource->getResource())) {
246 1
					return true;
247
				}
248
			}
249
		}
250
		
251 4
		return false;
252
	}
253
	
254
	/**
255
	 * ObjectInterface
256
	 */
257
	
258
	/**
259
	 * @inheritDoc
260
	 */
261 30
	public function isEmpty() {
262 30
		if ($this->type === RelationshipObject::TO_ONE && $this->resource !== null) {
263 22
			return false;
264
		}
265 10
		if ($this->type === RelationshipObject::TO_MANY && $this->resources !== []) {
266 5
			return false;
267
		}
268 6
		if ($this->links !== null && $this->links->isEmpty() === false) {
269 1
			return false;
270
		}
271 6
		if ($this->meta !== null && $this->meta->isEmpty() === false) {
272 1
			return false;
273
		}
274 6
		if ($this->hasAtMembers()) {
275 1
			return false;
276
		}
277 6
		if ($this->hasExtensionMembers()) {
278 1
			return false;
279
		}
280
		
281 6
		return true;
282
	}
283
	
284
	/**
285
	 * @inheritDoc
286
	 */
287 46
	public function toArray() {
288 46
		$array = [];
289
		
290 46
		if ($this->hasAtMembers()) {
291 1
			$array = array_merge($array, $this->getAtMembers());
292
		}
293 46
		if ($this->hasExtensionMembers()) {
294 1
			$array = array_merge($array, $this->getExtensionMembers());
295
		}
296
		
297 46
		if ($this->links !== null && $this->links->isEmpty() === false) {
298 9
			$array['links'] = $this->links->toArray();
299
		}
300 46
		if ($this->type === RelationshipObject::TO_ONE) {
301 37
			$array['data'] = null;
302 37
			if ($this->resource !== null) {
303 30
				$array['data'] = $this->resource->getResource($identifierOnly=true)->toArray();
304
			}
305
		}
306 46
		if ($this->type === RelationshipObject::TO_MANY) {
307 12
			$array['data'] = [];
308 12
			foreach ($this->resources as $resource) {
309 9
				$array['data'][] = $resource->getResource($identifierOnly=true)->toArray();
310
			}
311
		}
312 46
		if ($this->meta !== null && $this->meta->isEmpty() === false) {
313 6
			$array['meta'] = $this->meta->toArray();
314
		}
315
		
316 46
		return $array;
317
	}
318
	
319
	/**
320
	 * RecursiveResourceContainerInterface
321
	 */
322
	
323
	/**
324
	 * @inheritDoc
325
	 */
326 17
	public function getNestedContainedResourceObjects() {
327 17
		if ($this->isEmpty()) {
328 2
			return [];
329
		}
330
		
331 16
		$resources       = ($this->type === RelationshipObject::TO_ONE) ? [$this->resource] : $this->resources;
332 16
		$resourceObjects = [];
333
		
334 16
		foreach ($resources as $resource) {
335 16
			if ($resource->getResource() instanceof ResourceObject === false) {
336 3
				continue;
337
			}
338
			
339
			/** @var ResourceObject */
340 16
			$resourceObject = $resource->getResource();
341
			
342 16
			if ($resourceObject->hasIdentifierPropertiesOnly()) {
343 4
				continue;
344
			}
345
			
346 14
			$resourceObjects[] = $resourceObject;
347 14
			$resourceObjects   = array_merge($resourceObjects, $resourceObject->getNestedContainedResourceObjects());
348
		}
349
		
350 16
		return $resourceObjects;
351
	}
352
}
353