Test Failed
Pull Request — main (#64)
by Lode
03:16
created

getNestedContainedResourceObjects()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
c 0
b 0
f 0
nc 9
nop 0
dl 0
loc 25
rs 9.2222
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 {
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
53
	 * @param  array  $links    optional
54
	 * @param  array  $meta     optional
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
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
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
132
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
133
	 */
134
	public function setSelfLink($href, array $meta=[]) {
135
		$this->addLink('self', $href, $meta);
136
	}
137
	
138
	/**
139
	 * @param string $href
140
	 * @param array  $meta optional, if given a LinkObject is added, otherwise a link string is added
141
	 */
142
	public function setRelatedLink($href, array $meta=[]) {
143
		$this->addLink('related', $href, $meta);
144
	}
145
	
146
	/**
147
	 * @inheritDoc
148
	 * 
149
	 * @throws InputException if used on a to-one relationship
150
	 */
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
172
	 * @param mixed  $value
173
	 */
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
190
	 * 
191
	 * @throws InputException if used on a to-many relationship, use {@see ->addResource()} instead
192
	 */
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
205
	 * 
206
	 * @throws InputException if used on a to-one relationship, use {@see ->setResource()} instead
207
	 */
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
218
	 */
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
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
	 */
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
		if ($this->links !== null && $this->links->isEmpty() === false) {
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
	 */
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
	 */
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