Completed
Branch next (29fadd)
by Neomerx
03:15
created

DocumentWriter   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 94.23%

Importance

Changes 0
Metric Value
dl 0
loc 289
ccs 98
cts 104
cp 0.9423
rs 9.52
c 0
b 0
f 0
wmc 36
lcom 1
cbo 6

16 Methods

Rating   Name   Duplication   Size   Complexity  
A setNullToData() 0 8 1
A addIdentifierToData() 0 6 1
A addResourceToData() 0 8 1
A addResourceToIncluded() 0 18 2
A reset() 0 6 1
A hasNotBeenAdded() 0 4 1
A registerResource() 0 6 1
A getIdentifierRepresentation() 0 14 2
A getIdentifierRepresentationFromResource() 0 11 2
A getAttributesRepresentation() 0 13 3
A getRelationshipsRepresentation() 0 11 3
A getRelationshipRepresentation() 0 21 4
A getRelationshipDataRepresentation() 0 20 5
B getResourceRepresentation() 0 31 6
A addToData() 0 12 2
A addToIncluded() 0 4 1
1
<?php declare(strict_types=1);
2
3
namespace Neomerx\JsonApi\Representation;
4
5
/**
6
 * Copyright 2015-2019 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Neomerx\JsonApi\Contracts\Parser\IdentifierInterface;
22
use Neomerx\JsonApi\Contracts\Parser\RelationshipDataInterface;
23
use Neomerx\JsonApi\Contracts\Parser\RelationshipInterface;
24
use Neomerx\JsonApi\Contracts\Parser\ResourceInterface;
25
use Neomerx\JsonApi\Contracts\Representation\DocumentWriterInterface;
26
use Neomerx\JsonApi\Contracts\Representation\FieldSetFilterInterface;
27
use Neomerx\JsonApi\Contracts\Schema\DocumentInterface;
28
29
/**
30
 * @package Neomerx\JsonApi
31
 */
32
class DocumentWriter extends BaseWriter implements DocumentWriterInterface
33
{
34
    /**
35
     * @var array
36
     */
37
    private $addedResources;
38
39
    /**
40
     * @inheritdoc
41
     */
42 2
    public function setNullToData(): DocumentWriterInterface
43
    {
44
        // check data has not been added yet
45 2
        assert(isset($this->data[DocumentInterface::KEYWORD_DATA]) === false);
46 2
        $this->data[DocumentInterface::KEYWORD_DATA] = null;
47
48 2
        return $this;
49
    }
50
51
    /**
52
     * @inheritdoc
53
     */
54 4
    public function addIdentifierToData(IdentifierInterface $identifier): DocumentWriterInterface
55
    {
56 4
        $this->addToData($this->getIdentifierRepresentation($identifier));
57
58 4
        return $this;
59
    }
60
61
    /**
62
     * @inheritdoc
63
     */
64 55
    public function addResourceToData(
65
        ResourceInterface $resource,
66
        FieldSetFilterInterface $filter
67
    ): DocumentWriterInterface {
68 55
        $this->addToData($this->getResourceRepresentation($resource, $filter));
69
70 55
        return $this;
71
    }
72
73
    /**
74
     * @inheritdoc
75
     */
76 21
    public function addResourceToIncluded(
77
        ResourceInterface $resource,
78
        FieldSetFilterInterface $filter
79
    ): DocumentWriterInterface {
80
        // We track resources only in included section to avoid duplicates there.
81
        // If those resources duplicate main it is not bad because if we remove them
82
        // (and sometimes we would have to rollback and remove some of them if we meet it in the main resources)
83
        // the client app will have to search them not only in included section but in the main as well.
84
        //
85
        // The spec seems to be OK with it.
86
87 21
        if ($this->hasNotBeenAdded($resource) === true) {
88 21
            $this->registerResource($resource);
89 21
            $this->addToIncluded($this->getResourceRepresentation($resource, $filter));
90
        }
91
92 20
        return $this;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98 69
    protected function reset(): void
99
    {
100 69
        parent::reset();
101
102 69
        $this->addedResources = [];
103 69
    }
104
105
    /**
106
     * If full resource has not been added yet either to includes section.
107
     *
108
     * @param ResourceInterface $resource
109
     *
110
     * @return bool
111
     */
112 21
    protected function hasNotBeenAdded(ResourceInterface $resource): bool
113
    {
114 21
        return isset($this->addedResources[$resource->getId()][$resource->getType()]) === false;
115
    }
116
117
    /**
118
     * @param ResourceInterface $resource
119
     *
120
     * @return void
121
     */
122 21
    protected function registerResource(ResourceInterface $resource): void
123
    {
124 21
        assert($this->hasNotBeenAdded($resource));
125
126 21
        $this->addedResources[$resource->getId()][$resource->getType()] = true;
127 21
    }
128
129
    /**
130
     * @param IdentifierInterface $identifier
131
     *
132
     * @return array
133
     */
134 28
    protected function getIdentifierRepresentation(IdentifierInterface $identifier): array
135
    {
136
        // it's odd not to have actual ID for identifier (which is OK for newly created resource).
137 28
        assert($identifier->getId() !== null);
138
139 28
        return $identifier->hasIdentifierMeta() === false ? [
140 28
            DocumentInterface::KEYWORD_TYPE => $identifier->getType(),
141 28
            DocumentInterface::KEYWORD_ID   => $identifier->getId(),
142
        ] : [
143
            DocumentInterface::KEYWORD_TYPE => $identifier->getType(),
144
            DocumentInterface::KEYWORD_ID   => $identifier->getId(),
145 28
            DocumentInterface::KEYWORD_META => $identifier->getIdentifierMeta(),
146
        ];
147
    }
148
149
    /**
150
     * @param ResourceInterface $resource
151
     *
152
     * @return array
153
     */
154 20
    protected function getIdentifierRepresentationFromResource(ResourceInterface $resource): array
155
    {
156 20
        return $resource->hasIdentifierMeta() === false ? [
157 20
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
158 20
            DocumentInterface::KEYWORD_ID   => $resource->getId(),
159
        ] : [
160
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
161
            DocumentInterface::KEYWORD_ID   => $resource->getId(),
162 20
            DocumentInterface::KEYWORD_META => $resource->getIdentifierMeta(),
163
        ];
164
    }
165
166
    /**
167
     * @param iterable $attributes
168
     *
169
     * @return array
170
     */
171 56
    protected function getAttributesRepresentation(iterable $attributes): array
172
    {
173 56
        if (is_array($attributes) === true) {
174
            return $attributes;
175
        }
176
177 56
        $representation = [];
178 56
        foreach ($attributes as $name => $value) {
179 56
            $representation[$name] = $value;
180
        }
181
182 56
        return $representation;
183
    }
184
185
    /**
186
     * @param iterable $relationships
187
     *
188
     * @return array
189
     */
190 56
    protected function getRelationshipsRepresentation(iterable $relationships): array
191
    {
192 56
        $representation = [];
193 56
        foreach ($relationships as $name => $relationship) {
194 38
            assert(is_string($name) === true && empty($name) === false);
195 38
            assert($relationship instanceof RelationshipInterface);
196 38
            $representation[$name] = $this->getRelationshipRepresentation($relationship);
197
        }
198
199 56
        return $representation;
200
    }
201
202
    /**
203
     * @param RelationshipInterface $relationship
204
     *
205
     * @return array
206
     */
207 38
    protected function getRelationshipRepresentation(RelationshipInterface $relationship): array
208
    {
209 38
        $representation = [];
210
211 38
        if ($relationship->hasLinks() === true) {
212 21
            $representation[DocumentInterface::KEYWORD_LINKS] =
213 21
                $this->getLinksRepresentation($this->getUrlPrefix(), $relationship->getLinks());
214
        }
215
216 38
        if ($relationship->hasData() === true) {
217 32
            $representation[DocumentInterface::KEYWORD_DATA] = $this->getRelationshipDataRepresentation(
218 32
                $relationship->getData()
219
            );
220
        }
221
222 38
        if ($relationship->hasMeta() === true) {
223 1
            $representation[DocumentInterface::KEYWORD_META] = $relationship->getMeta();
224
        }
225
226 38
        return $representation;
227
    }
228
229
    /**
230
     * @param RelationshipDataInterface $data
231
     *
232
     * @return array|null
233
     */
234 32
    protected function getRelationshipDataRepresentation(RelationshipDataInterface $data): ?array
235
    {
236 32
        if ($data->isResource() === true) {
237 20
            return $this->getIdentifierRepresentationFromResource($data->getResource());
238 29
        } elseif ($data->isIdentifier() === true) {
239 1
            return $this->getIdentifierRepresentation($data->getIdentifier());
240 28
        } elseif ($data->isCollection() === true) {
241 27
            $representation = [];
242 27
            foreach ($data->getIdentifiers() as $identifier) {
243 24
                assert($identifier instanceof IdentifierInterface);
244 24
                $representation[] = $this->getIdentifierRepresentation($identifier);
245
            }
246
247 27
            return $representation;
248
        }
249
250 5
        assert($data->isNull() === true);
251
252 5
        return null;
253
    }
254
255
    /**
256
     * @param ResourceInterface       $resource
257
     * @param FieldSetFilterInterface $filter
258
     *
259
     * @return array
260
     */
261 56
    protected function getResourceRepresentation(ResourceInterface $resource, FieldSetFilterInterface $filter): array
262
    {
263
        $representation = [
264 56
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
265
        ];
266
267 56
        if (($index = $resource->getId()) !== null) {
268 55
            $representation[DocumentInterface::KEYWORD_ID] = $index;
269
        }
270
271 56
        $attributes = $this->getAttributesRepresentation($filter->getAttributes($resource));
272 56
        if (empty($attributes) === false) {
273 56
            $representation[DocumentInterface::KEYWORD_ATTRIBUTES] = $attributes;
274
        }
275
276 56
        $relationships = $this->getRelationshipsRepresentation($filter->getRelationships($resource));
277 56
        if (empty($relationships) === false) {
278 38
            $representation[DocumentInterface::KEYWORD_RELATIONSHIPS] = $relationships;
279
        }
280
281 56
        if ($resource->hasLinks() === true) {
282 55
            $representation[DocumentInterface::KEYWORD_LINKS] =
283 55
                $this->getLinksRepresentation($this->getUrlPrefix(), $resource->getLinks());
284
        }
285
286 56
        if ($resource->hasResourceMeta() === true) {
287
            $representation[DocumentInterface::KEYWORD_META] = $resource->getResourceMeta();
288
        }
289
290 56
        return $representation;
291
    }
292
293
    /**
294
     * @param array $representation
295
     *
296
     * @return void
297
     */
298 59
    private function addToData(array $representation): void
299
    {
300 59
        if ($this->isDataAnArray() === true) {
301 16
            $this->data[DocumentInterface::KEYWORD_DATA][] = $representation;
302
303 16
            return;
304
        }
305
306
        // check data has not been added yet
307 44
        assert(array_key_exists(DocumentInterface::KEYWORD_DATA, $this->data) === false);
308 44
        $this->data[DocumentInterface::KEYWORD_DATA] = $representation;
309 44
    }
310
311
    /**
312
     * @param array $representation
313
     *
314
     * @return void
315
     */
316 20
    private function addToIncluded(array $representation): void
317
    {
318 20
        $this->data[DocumentInterface::KEYWORD_INCLUDED][] = $representation;
319 20
    }
320
}
321