Completed
Branch master (fb6fda)
by
unknown
03:39
created

Document::addErrors()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
cc 3
eloc 4
nc 4
nop 1
crap 3
1
<?php namespace Neomerx\JsonApi\Document;
2
3
/**
4
 * Copyright 2015 [email protected] (www.neomerx.com)
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use \Closure;
20
use \Psr\Log\LoggerAwareTrait;
21
use \Psr\Log\LoggerAwareInterface;
22
use \Neomerx\JsonApi\Factories\Exceptions;
23
use \Neomerx\JsonApi\Contracts\Document\ErrorInterface;
24
use \Neomerx\JsonApi\Contracts\Document\DocumentInterface;
25
use \Neomerx\JsonApi\Document\Presenters\ElementPresenter;
26
use \Neomerx\JsonApi\Contracts\Schema\ResourceObjectInterface;
27
use \Neomerx\JsonApi\Contracts\Schema\RelationshipObjectInterface;
28
29
/**
30
 * @package Neomerx\JsonApi
31
 */
32
class Document implements DocumentInterface, LoggerAwareInterface
33
{
34
    use LoggerAwareTrait;
35
36
    /**
37
     * @var array
38
     */
39
    private $errors;
40
41
    /**
42
     * @var array|object|null
43
     */
44
    private $meta;
45
46
    /**
47
     * @var array|null|string
48
     */
49
    private $links;
50
51
    /**
52
     * @var array
53
     */
54
    private $hasBeenMetAlready;
55
56
    /**
57
     * @var array|null
58
     */
59
    private $included;
60
61
    /**
62
     * @var int
63
     */
64
    private $includedIndex = 0;
65
66
    /**
67
     * @var array
68
     */
69
    private $includedResources = [];
70
71
    /**
72
     * @var array|null
73
     */
74
    private $version;
75
76
    /**
77
     * @var array|null
78
     */
79
    private $data;
80
81
    /**
82
     * @var array
83
     */
84
    private $bufferForData = [];
85
86
    /**
87
     * @var array
88
     */
89
    private $bufferForIncluded = [];
90
91
    /**
92
     * If original data were in array.
93
     *
94
     * @var bool|null
95
     */
96
    private $isDataArrayed;
97
98
    /**
99
     * @var ElementPresenter
100
     */
101
    private $presenter;
102
103
    /**
104
     * @var bool
105
     */
106
    private $showData = true;
107
108
    /**
109
     * @var string|null
110
     */
111
    private $urlPrefix;
112
113
    /**
114
     * Constructor.
115
     */
116 94
    public function __construct()
117
    {
118 94
        $this->presenter = new ElementPresenter($this);
119 94
    }
120
121
    /**
122
     * @inheritdoc
123
     */
124 8
    public function setDocumentLinks($links)
125
    {
126 8
        $this->links = $this->presenter->getLinksRepresentation($this->urlPrefix, $links);
0 ignored issues
show
Bug introduced by
It seems like $links defined by parameter $links on line 124 can also be of type array<string,object<Neom...ocument\LinkInterface>>; however, Neomerx\JsonApi\Document...etLinksRepresentation() does only seem to accept array<string,object<Neom...ma\LinkInterface>>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
127 8
    }
128
129
    /**
130
     * @inheritdoc
131
     */
132 6
    public function setMetaToDocument($meta)
133
    {
134 6
        (is_object($meta) === true || is_array($meta) === true) ?: Exceptions::throwInvalidArgument('meta', $meta);
135 6
        $this->meta = $meta;
136 6
    }
137
138
    /**
139
     * @inheritdoc
140
     */
141 27
    public function addToIncluded(ResourceObjectInterface $resource)
142
    {
143 27
        $idx  = $resource->getId();
144 27
        $type = $resource->getType();
145 27
        if (isset($this->hasBeenMetAlready[$type][$idx]) === false) {
146 26
            $this->bufferForIncluded[$type][$idx] = $this->presenter->convertIncludedResourceToArray($resource);
147 26
            $this->hasBeenMetAlready[$type][$idx] = true;
148 26
        }
149 27
    }
150
151
    /**
152
     * @inheritdoc
153
     */
154 66
    public function addToData(ResourceObjectInterface $resource)
155
    {
156
        // check if 'not-arrayed' data were added you cannot add to 'non-array' data section anymore
157 66
        ($this->isDataArrayed === true || $this->isDataArrayed === null) ?: Exceptions::throwLogicException();
158
159 66
        $this->isDataArrayed !== null ?: $this->isDataArrayed = $resource->isInArray();
160
161
        // check all resources have the same isInArray flag
162 66
        ($this->isDataArrayed === $resource->isInArray()) ?: Exceptions::throwLogicException();
163
164 66
        $idx  = $resource->getId();
165 66
        $type = $resource->getType();
166
167 66
        isset($this->bufferForData[$type][$idx]) === false ?: Exceptions::throwLogicException();
168
169 66
        $this->bufferForData[$type][$idx] = $this->presenter->convertDataResourceToArray($resource, true);
170 64
        $this->hasBeenMetAlready[$type][$idx] = true;
171
172
        // check if resource has already been added to included
173
        // (for example as related resource of one of the previous main resources)
174 64
        if (isset($this->includedResources[$type][$idx]) === true) {
175 1
            $includedIndex = $this->includedResources[$type][$idx];
176
177
            // remove duplicate from 'included' (leave only in main resources)
178 1
            unset($this->included[$includedIndex]);
179 1
        }
180 64
    }
181
182
    /**
183
     * @inheritdoc
184
     */
185 6
    public function setEmptyData()
186
    {
187 6
        $this->data = [];
188 6
    }
189
190
    /**
191
     * @inheritdoc
192
     */
193 3
    public function setNullData()
194
    {
195 3
        $this->data = null;
196 3
    }
197
198
    /**
199
     * @inheritdoc
200
     */
201 30
    public function addRelationshipToData(
202
        ResourceObjectInterface $parent,
203
        RelationshipObjectInterface $relationship,
204
        ResourceObjectInterface $resource
205
    ) {
206 30
        $this->presenter->addRelationshipTo($this->bufferForData, $parent, $relationship, $resource);
207 30
    }
208
209
    /**
210
     * @inheritdoc
211
     */
212 17
    public function addRelationshipToIncluded(
213
        ResourceObjectInterface $parent,
214
        RelationshipObjectInterface $relationship,
215
        ResourceObjectInterface $resource
216
    ) {
217 17
        $this->presenter->addRelationshipTo($this->bufferForIncluded, $parent, $relationship, $resource);
218 17
    }
219
220
    /**
221
     * @inheritdoc
222
     */
223 5
    public function addEmptyRelationshipToData(
224
        ResourceObjectInterface $parent,
225
        RelationshipObjectInterface $relationship
226
    ) {
227 5
        $this->presenter->setRelationshipTo($this->bufferForData, $parent, $relationship, []);
228 4
    }
229
230
    /**
231
     * @inheritdoc
232
     */
233 9
    public function addNullRelationshipToData(
234
        ResourceObjectInterface $parent,
235
        RelationshipObjectInterface $relationship
236
    ) {
237 9
        $this->presenter->setRelationshipTo($this->bufferForData, $parent, $relationship, null);
238 9
    }
239
240
    /**
241
     * @inheritdoc
242
     */
243 3
    public function addEmptyRelationshipToIncluded(
244
        ResourceObjectInterface $parent,
245
        RelationshipObjectInterface $relationship
246
    ) {
247 3
        $this->presenter->setRelationshipTo($this->bufferForIncluded, $parent, $relationship, []);
248 3
    }
249
250
    /**
251
     * @inheritdoc
252
     */
253 6
    public function addNullRelationshipToIncluded(
254
        ResourceObjectInterface $parent,
255
        RelationshipObjectInterface $relationship
256
    ) {
257 6
        $this->presenter->setRelationshipTo($this->bufferForIncluded, $parent, $relationship, null);
258 6
    }
259
260
    /**
261
     * @inheritdoc
262
     */
263 69
    public function setResourceCompleted(ResourceObjectInterface $resource)
264
    {
265 69
        $idx  = $resource->getId();
266 69
        $type = $resource->getType();
267
268 69
        $foundInData     = isset($this->bufferForData[$type][$idx]);
269 69
        $foundInIncluded = isset($this->bufferForIncluded[$type][$idx]);
270
271
        $addMeta = function (array $representation, Closure $getMetaClosure) {
272 69
            if (empty($representation[self::KEYWORD_RELATIONSHIPS]) === true) {
273
                // if no relationships have been added remove empty placeholder
274 30
                unset($representation[self::KEYWORD_RELATIONSHIPS]);
275 30
            } else {
276
                // relationship might have meta
277 47
                $relShipsMeta = $getMetaClosure();
278 47
                if (empty($relShipsMeta) === false) {
279 3
                    $representation[self::KEYWORD_RELATIONSHIPS][self::KEYWORD_META] = $relShipsMeta;
280 3
                }
281
            }
282
283 69
            return $representation;
284 69
        };
285
286 69
        if ($foundInData === true) {
287 62
            $representation = $this->bufferForData[$type][$idx];
288 62
            unset($this->bufferForData[$type][$idx]);
289
290
            $this->data[] = $addMeta($representation, function () use ($resource) {
291 39
                return $resource->getRelationshipsPrimaryMeta();
292 62
            });
293 62
        }
294
295 69
        if ($foundInIncluded === true) {
296 25
            $representation = $this->bufferForIncluded[$type][$idx];
297 25
            unset($this->bufferForIncluded[$type][$idx]);
298
299
            $this->included[] = $addMeta($representation, function () use ($resource) {
300 21
                return $resource->getRelationshipsInclusionMeta();
301 25
            });
302
            // remember we added (type, id) at index
303 25
            $this->includedResources[$type][$idx] = $this->includedIndex;
304 25
            $this->includedIndex++;
305 25
        }
306 69
    }
307
308
    /**
309
     * @inheritdoc
310
     */
311 89
    public function getDocument()
312
    {
313 89
        if ($this->errors !== null) {
314 7
            return [self::KEYWORD_ERRORS => $this->errors];
315
        }
316
317 82
        $document = array_filter([
318 82
            self::KEYWORD_JSON_API => $this->version,
319 82
            self::KEYWORD_META     => $this->meta,
320 82
            self::KEYWORD_LINKS    => $this->links,
321 82
            self::KEYWORD_DATA     => true, // this field wont be filtered
322 82
            self::KEYWORD_INCLUDED => empty($this->included) === true ? null : array_values($this->included),
323
        ], function ($value) {
324 82
            return $value !== null;
325 82
        });
326
327 82
        if ($this->showData === true) {
328 79
            $isDataNotArray               = ($this->isDataArrayed === false && empty($this->data) === false);
329 79
            $document[self::KEYWORD_DATA] = ($isDataNotArray ? $this->data[0] : $this->data);
330 79
        } else {
331 3
            unset($document[self::KEYWORD_DATA]);
332
        }
333
334 82
        return $document;
335
    }
336
337
    /**
338
     * @inheritdoc
339
     */
340 2
    public function addJsonApiVersion($version, $meta = null)
341
    {
342 2
        $this->version = $meta === null ?
343 2
            [self::KEYWORD_VERSION => $version] : [self::KEYWORD_VERSION => $version, self::KEYWORD_META => $meta];
344 2
    }
345
346
    /**
347
     * @inheritdoc
348
     */
349 3
    public function unsetData()
350
    {
351 3
        $this->showData = false;
352 3
    }
353
354
    /**
355
     * @inheritdoc
356
     */
357 6
    public function addError(ErrorInterface $error)
358
    {
359 6
        $errorId = (($errorId = $error->getId()) === null ? null : (string)$errorId);
360
361 6
        $representation = array_filter([
362 6
            self::KEYWORD_ERRORS_ID     => $errorId,
363 6
            self::KEYWORD_ERRORS_LINKS  => $this->presenter
364 6
                ->getLinksRepresentation($this->urlPrefix, $error->getLinks()),
365 6
            self::KEYWORD_ERRORS_STATUS => $error->getStatus(),
366 6
            self::KEYWORD_ERRORS_CODE   => $error->getCode(),
367 6
            self::KEYWORD_ERRORS_TITLE  => $error->getTitle(),
368 6
            self::KEYWORD_ERRORS_DETAIL => $error->getDetail(),
369 6
            self::KEYWORD_ERRORS_SOURCE => $error->getSource(),
370 6
            self::KEYWORD_ERRORS_META   => $error->getMeta(),
371 6
        ], function ($value) {
372 6
            return $value !== null;
373 6
        });
374
375 6
        $this->errors[] = (object)$representation;
376 6
    }
377
378
    /**
379
     * @inheritdoc
380
     */
381 3
    public function addErrors($errors)
382
    {
383 3
        empty($this->errors) === false ?: $this->errors = [];
384
385 3
        foreach ($errors as $error) {
386 2
            $this->addError($error);
387 3
        }
388 3
    }
389
390
    /**
391
     * @inheritdoc
392
     */
393 45
    public function setUrlPrefix($prefix)
394
    {
395 45
        $this->urlPrefix = (string)$prefix;
396 45
    }
397
398
    /**
399
     * Get URL prefix.
400
     *
401
     * @return null|string
402
     */
403 64
    public function getUrlPrefix()
404
    {
405 64
        return $this->urlPrefix;
406
    }
407
}
408