assertRelationshipNameAndDescription()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 1
nop 2
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 3
rs 9.8333
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Neomerx\JsonApi\Parser;
4
5
/**
6
 * Copyright 2015-2020 [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\Factories\FactoryInterface;
22
use Neomerx\JsonApi\Contracts\Parser\EditableContextInterface;
23
use Neomerx\JsonApi\Contracts\Parser\ParserInterface;
24
use Neomerx\JsonApi\Contracts\Parser\ResourceInterface;
25
use Neomerx\JsonApi\Contracts\Schema\LinkInterface;
26
use Neomerx\JsonApi\Contracts\Schema\PositionInterface;
27
use Neomerx\JsonApi\Contracts\Schema\SchemaContainerInterface;
28
use Neomerx\JsonApi\Contracts\Schema\SchemaInterface;
29
use Neomerx\JsonApi\Parser\RelationshipData\ParseRelationshipDataTrait;
30
use Neomerx\JsonApi\Parser\RelationshipData\ParseRelationshipLinksTrait;
31
32
/**
33
 * @package Neomerx\JsonApi
34
 */
35
class IdentifierAndResource implements ResourceInterface
36
{
37
    use ParseRelationshipDataTrait, ParseRelationshipLinksTrait;
38
39
    /** @var string */
40
    public const MSG_NO_SCHEMA_FOUND = 'No Schema found for resource `%s` at path `%s`.';
41
42
    /** @var string */
43
    public const MSG_INVALID_OPERATION = 'Invalid operation.';
44
45
    /**
46
     * @var EditableContextInterface
47
     */
48
    private $context;
49
50
    /**
51
     * @var PositionInterface
52
     */
53
    private $position;
54
55
    /**
56
     * @var FactoryInterface
57
     */
58
    private $factory;
59
60
    /**
61
     * @var SchemaContainerInterface
62
     */
63
    private $schemaContainer;
64
65
    /**
66
     * @var SchemaInterface
67
     */
68
    private $schema;
69
70
    /**
71
     * @var mixed
72
     */
73
    private $data;
74
75
    /**
76
     * @var string
77
     */
78
    private $index;
79
80
    /**
81
     * @var string
82
     */
83
    private $type;
84
85
    /**
86
     * @var null|array
87
     */
88
    private $links = null;
89
90
    /**
91
     * @var null|array
92
     */
93
    private $relationshipsCache = null;
94
95
    /**
96
     * @param EditableContextInterface $context
97
     * @param PositionInterface        $position
98
     * @param FactoryInterface         $factory
99
     * @param SchemaContainerInterface $container
100
     * @param mixed                    $data
101
     */
102 65
    public function __construct(
103
        EditableContextInterface $context,
104
        PositionInterface $position,
105
        FactoryInterface $factory,
106
        SchemaContainerInterface $container,
107
        $data
108
    ) {
109 65
        \assert($position->getLevel() >= ParserInterface::ROOT_LEVEL);
110
111 65
        $schema = $container->getSchema($data);
112
113 65
        $this->context         = $context;
114 65
        $this->position        = $position;
115 65
        $this->factory         = $factory;
116 65
        $this->schemaContainer = $container;
117 65
        $this->schema          = $schema;
118 65
        $this->data            = $data;
119 65
        $this->index           = $schema->getId($data);
120 65
        $this->type            = $schema->getType();
121 65
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126 65
    public function getPosition(): PositionInterface
127
    {
128 65
        return $this->position;
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134 65
    public function getId(): ?string
135
    {
136 65
        return $this->index;
137
    }
138
139
    /**
140
     * @inheritdoc
141
     */
142 65
    public function getType(): string
143
    {
144 65
        return $this->type;
145
    }
146
147
    /**
148
     * @inheritdoc
149
     */
150 35
    public function hasIdentifierMeta(): bool
151
    {
152 35
        return $this->schema->hasIdentifierMeta($this->data);
153
    }
154
155
    /**
156
     * @inheritdoc
157
     */
158 2
    public function getIdentifierMeta()
159
    {
160 2
        return $this->schema->getIdentifierMeta($this->data);
161
    }
162
163
    /**
164
     * @inheritdoc
165
     */
166 62
    public function getAttributes(): iterable
167
    {
168 62
        $this->getContext()->setPosition($this->getPosition());
169
170 62
        return $this->schema->getAttributes($this->data, $this->getContext());
171
    }
172
173
    /**
174
     * @inheritdoc
175
     *
176
     * @SuppressWarnings(PHPMD.UndefinedVariable) PHPMD currently do not support `list` in `[]` syntax
177
     */
178 64
    public function getRelationships(): iterable
179
    {
180 64
        if ($this->relationshipsCache !== null) {
181 61
            yield from $this->relationshipsCache;
182
183 60
            return;
184
        }
185
186 64
        $this->relationshipsCache = [];
187
188 64
        $currentPath    = $this->position->getPath();
189 64
        $nextLevel      = $this->position->getLevel() + 1;
190 64
        $nextPathPrefix = empty($currentPath) === true ? '' : $currentPath . PositionInterface::PATH_SEPARATOR;
191 64
        $this->getContext()->setPosition($this->getPosition());
192 64
        foreach ($this->schema->getRelationships($this->data, $this->getContext()) as $name => $description) {
193 49
            \assert($this->assertRelationshipNameAndDescription($name, $description) === true);
194
195 49
            [$hasData, $relationshipData, $nextPosition] = $this->parseRelationshipData(
0 ignored issues
show
Bug introduced by
The variable $hasData does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $relationshipData does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $nextPosition does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
196 49
                $this->factory,
197 49
                $this->schemaContainer,
198 49
                $this->getContext(),
199 49
                $this->type,
200 49
                $name,
201 49
                $description,
202 49
                $nextLevel,
203 49
                $nextPathPrefix
204
            );
205
206
            [$hasLinks, $links] =
0 ignored issues
show
Bug introduced by
The variable $hasLinks does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $links does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
207 49
                $this->parseRelationshipLinks($this->schema, $this->data, $name, $description);
208
209 49
            $hasMeta = \array_key_exists(SchemaInterface::RELATIONSHIP_META, $description);
210 49
            $meta    = $hasMeta === true ? $description[SchemaInterface::RELATIONSHIP_META] : null;
211
212 49
            \assert(
213 49
                $hasData || $hasMeta || $hasLinks,
214 49
                "Relationship `$name` for type `" . $this->getType() .
215 49
                '` MUST contain at least one of the following: links, data or meta.'
216
            );
217
218 49
            $relationship = $this->factory->createRelationship(
219 49
                $nextPosition,
220 49
                $hasData,
221 49
                $relationshipData,
222 49
                $hasLinks,
223 49
                $links,
224 49
                $hasMeta,
225 49
                $meta
226
            );
227
228 49
            $this->relationshipsCache[$name] = $relationship;
229
230 49
            yield $name => $relationship;
231
        }
232 64
    }
233
234
    /**
235
     * @inheritdoc
236
     */
237 62
    public function hasLinks(): bool
238
    {
239 62
        $this->cacheLinks();
240
241 62
        return empty($this->links) === false;
242
    }
243
244
    /**
245
     * @inheritdoc
246
     */
247 61
    public function getLinks(): iterable
248
    {
249 61
        $this->cacheLinks();
250
251 61
        return $this->links;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->links; of type null|array adds the type array to the return on line 251 which is incompatible with the return type declared by the interface Neomerx\JsonApi\Contract...urceInterface::getLinks of type Neomerx\JsonApi\Contracts\Parser\iterable.
Loading history...
252
    }
253
254
    /**
255
     * @inheritdoc
256
     */
257 62
    public function hasResourceMeta(): bool
258
    {
259 62
        return $this->schema->hasResourceMeta($this->data);
260
    }
261
262
    /**
263
     * @inheritdoc
264
     */
265 1
    public function getResourceMeta()
266
    {
267 1
        return $this->schema->getResourceMeta($this->data);
268
    }
269
270
    /**
271
     * @return EditableContextInterface
272
     */
273 65
    protected function getContext(): EditableContextInterface
274
    {
275 65
        return $this->context;
276
    }
277
278
    /**
279
     * Read and parse links from schema.
280
     */
281 62
    private function cacheLinks(): void
282
    {
283 62
        if ($this->links === null) {
284 62
            $this->links = [];
285 62
            foreach ($this->schema->getLinks($this->data) as $name => $link) {
286 61
                \assert(\is_string($name) === true && empty($name) === false);
287 61
                \assert($link instanceof LinkInterface);
288 61
                $this->links[$name] = $link;
289
            }
290
        }
291 62
    }
292
293
    /**
294
     * @param string $name
295
     * @param array  $description
296
     *
297
     * @return bool
298
     */
299 49
    private function assertRelationshipNameAndDescription(string $name, array $description): bool
300
    {
301 49
        \assert(
302 49
            \is_string($name) === true && empty($name) === false,
303 49
            "Relationship names for type `" . $this->getType() . '` should be non-empty strings.'
304
        );
305 49
        \assert(
306 49
            \is_array($description) === true && empty($description) === false,
307 49
            "Relationship `$name` for type `" . $this->getType() . '` should be a non-empty array.'
308
        );
309
310 49
        return true;
311
    }
312
}
313