Completed
Pull Request — master (#227)
by
unknown
04:33
created

getRelationshipDataRepresentation()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0909

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 1
dl 0
loc 20
ccs 11
cts 13
cp 0.8462
crap 5.0909
rs 9.2888
c 0
b 0
f 0
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 3
    public function setNullToData(): DocumentWriterInterface
43
    {
44
        // check data has not been added yet
45 3
        assert(isset($this->data[DocumentInterface::KEYWORD_DATA]) === false);
46 3
        $this->data[DocumentInterface::KEYWORD_DATA] = null;
47
48 3
        return $this;
49
    }
50
51
    /**
52
     * @inheritdoc
53
     */
54 6
    public function addIdentifierToData(IdentifierInterface $identifier): DocumentWriterInterface
55
    {
56 6
        $this->addToData($this->getIdentifierRepresentation($identifier));
57
58 6
        return $this;
59
    }
60
61
    /**
62
     * @inheritdoc
63
     */
64 53
    public function addResourceToData(
65
        ResourceInterface $resource,
66
        FieldSetFilterInterface $filter
67
    ): DocumentWriterInterface {
68 53
        $this->addToData($this->getResourceRepresentation($resource, $filter));
69
70 53
        return $this;
71
    }
72
73
    /**
74
     * @inheritdoc
75
     */
76 20
    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 20
        if ($this->hasNotBeenAdded($resource) === true) {
88 20
            $this->registerResource($resource);
89 20
            $this->addToIncluded($this->getResourceRepresentation($resource, $filter));
90
        }
91
92 19
        return $this;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98 70
    protected function reset(): void
99
    {
100 70
        parent::reset();
101
102 70
        $this->addedResources = [];
103 70
    }
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 20
    protected function hasNotBeenAdded(ResourceInterface $resource): bool
113
    {
114 20
        return isset($this->addedResources[$resource->getId()][$resource->getType()]) === false;
115
    }
116
117
    /**
118
     * @param ResourceInterface $resource
119
     *
120
     * @return void
121
     */
122 20
    protected function registerResource(ResourceInterface $resource): void
123
    {
124 20
        assert($this->hasNotBeenAdded($resource));
125
126 20
        $this->addedResources[$resource->getId()][$resource->getType()] = true;
127 20
    }
128
129
    /**
130
     * @param IdentifierInterface $identifier
131
     *
132
     * @return array
133
     */
134 29
    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 29
        assert($identifier->getId() !== null);
138
139 29
        return $identifier->hasIdentifierMeta() === false ? [
140 26
            DocumentInterface::KEYWORD_TYPE => $identifier->getType(),
141 26
            DocumentInterface::KEYWORD_ID   => $identifier->getId(),
142
        ] : [
143 3
            DocumentInterface::KEYWORD_TYPE => $identifier->getType(),
144 3
            DocumentInterface::KEYWORD_ID   => $identifier->getId(),
145 29
            DocumentInterface::KEYWORD_META => $identifier->getIdentifierMeta(),
146
        ];
147
    }
148
149
    /**
150
     * @param ResourceInterface $resource
151
     *
152
     * @return array
153
     */
154 19
    protected function getIdentifierRepresentationFromResource(ResourceInterface $resource): array
155
    {
156 19
        return $resource->hasIdentifierMeta() === false ? [
157 18
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
158 18
            DocumentInterface::KEYWORD_ID   => $resource->getId(),
159
        ] : [
160 1
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
161 1
            DocumentInterface::KEYWORD_ID   => $resource->getId(),
162 19
            DocumentInterface::KEYWORD_META => $resource->getIdentifierMeta(),
163
        ];
164
    }
165
166
    /**
167
     * @param iterable $attributes
168
     *
169
     * @return array
170
     */
171 54
    protected function getAttributesRepresentation(iterable $attributes): array
172
    {
173 54
        $representation = [];
174 54
        foreach ($attributes as $name => $value) {
175 54
            $representation[$name] = $value;
176
        }
177
178 54
        return $representation;
179
    }
180
181
    /**
182
     * @param iterable $relationships
183
     *
184
     * @return array
185
     */
186 54
    protected function getRelationshipsRepresentation(iterable $relationships): array
187
    {
188 54
        $representation = [];
189 54
        foreach ($relationships as $name => $relationship) {
190 36
            assert(is_string($name) === true && empty($name) === false);
191 36
            assert($relationship instanceof RelationshipInterface);
192 36
            $representation[$name] = $this->getRelationshipRepresentation($relationship);
193
        }
194
195 54
        return $representation;
196
    }
197
198
    /**
199
     * @param RelationshipInterface $relationship
200
     *
201
     * @return array
202
     */
203 36
    protected function getRelationshipRepresentation(RelationshipInterface $relationship): array
204
    {
205 36
        $representation = [];
206
207 36
        if ($relationship->hasLinks() === true) {
208 22
            $representation[DocumentInterface::KEYWORD_LINKS] =
209 22
                $this->getLinksRepresentation($this->getUrlPrefix(), $relationship->getLinks());
210
        }
211
212 36
        if ($relationship->hasData() === true) {
213 29
            $representation[DocumentInterface::KEYWORD_DATA] = $this->getRelationshipDataRepresentation(
214 29
                $relationship->getData()
215
            );
216
        }
217
218 36
        if ($relationship->hasMeta() === true) {
219 1
            $representation[DocumentInterface::KEYWORD_META] = $relationship->getMeta();
220
        }
221
222 36
        return $representation;
223
    }
224
225
    /**
226
     * @param RelationshipDataInterface $data
227
     *
228
     * @return array|null
229
     */
230 29
    protected function getRelationshipDataRepresentation(RelationshipDataInterface $data): ?array
231
    {
232 29
        if ($data->isResource() === true) {
233 19
            return $this->getIdentifierRepresentationFromResource($data->getResource());
234 26
        } elseif ($data->isIdentifier() === true) {
235 1
            return $this->getIdentifierRepresentation($data->getIdentifier());
236 25
        } elseif ($data->isCollection() === true) {
237 25
            $representation = [];
238 25
            foreach ($data->getIdentifiers() as $identifier) {
239 23
                assert($identifier instanceof IdentifierInterface);
240 23
                $representation[] = $this->getIdentifierRepresentation($identifier);
241
            }
242
243 25
            return $representation;
244
        }
245
246
        assert($data->isNull() === true);
247
248
        return null;
249
    }
250
251
    /**
252
     * @param ResourceInterface       $resource
253
     * @param FieldSetFilterInterface $filter
254
     *
255
     * @return array
256
     */
257 54
    protected function getResourceRepresentation(ResourceInterface $resource, FieldSetFilterInterface $filter): array
258
    {
259
        $representation = [
260 54
            DocumentInterface::KEYWORD_TYPE => $resource->getType(),
261
        ];
262
263 54
        if (($index = $resource->getId()) !== null) {
264 53
            $representation[DocumentInterface::KEYWORD_ID] = $index;
265
        }
266
267 54
        $attributes = $this->getAttributesRepresentation($filter->getAttributes($resource));
268 54
        if (empty($attributes) === false) {
269 54
            $representation[DocumentInterface::KEYWORD_ATTRIBUTES] = $attributes;
270
        }
271
272 54
        $relationships = $this->getRelationshipsRepresentation($filter->getRelationships($resource));
273 54
        if (empty($relationships) === false) {
274 36
            $representation[DocumentInterface::KEYWORD_RELATIONSHIPS] = $relationships;
275
        }
276
277 54
        if ($resource->hasLinks() === true) {
278 53
            $representation[DocumentInterface::KEYWORD_LINKS] =
279 53
                $this->getLinksRepresentation($this->getUrlPrefix(), $resource->getLinks());
280
        }
281
282 54
        if ($resource->hasResourceMeta() === true) {
283 1
            $representation[DocumentInterface::KEYWORD_META] = $resource->getResourceMeta();
284
        }
285
286 54
        return $representation;
287
    }
288
289
    /**
290
     * @param array $representation
291
     *
292
     * @return void
293
     */
294 59
    private function addToData(array $representation): void
295
    {
296 59
        if ($this->isDataAnArray() === true) {
297 17
            $this->data[DocumentInterface::KEYWORD_DATA][] = $representation;
298
299 17
            return;
300
        }
301
302
        // check data has not been added yet
303 45
        assert(array_key_exists(DocumentInterface::KEYWORD_DATA, $this->data) === false);
304 45
        $this->data[DocumentInterface::KEYWORD_DATA] = $representation;
305 45
    }
306
307
    /**
308
     * @param array $representation
309
     *
310
     * @return void
311
     */
312 19
    private function addToIncluded(array $representation): void
313
    {
314 19
        $this->data[DocumentInterface::KEYWORD_INCLUDED][] = $representation;
315 19
    }
316
}
317