Completed
Branch develop (5a7a06)
by Neomerx
04:07
created

IdentifierAndResource::getContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
cc 1
nc 1
nop 0
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php declare(strict_types=1);
2
3
namespace Neomerx\JsonApi\Parser;
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\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 64
    public function getRelationships(): iterable
177
    {
178 64
        if ($this->relationshipsCache !== null) {
179 61
            yield from $this->relationshipsCache;
180
181 60
            return;
182
        }
183
184 64
        $this->relationshipsCache = [];
185
186 64
        $currentPath    = $this->position->getPath();
187 64
        $nextLevel      = $this->position->getLevel() + 1;
188 64
        $nextPathPrefix = empty($currentPath) === true ? '' : $currentPath . PositionInterface::PATH_SEPARATOR;
189 64
        $this->getContext()->setPosition($this->getPosition());
190 64
        foreach ($this->schema->getRelationships($this->data, $this->getContext()) as $name => $description) {
191 49
            \assert($this->assertRelationshipNameAndDescription($name, $description) === true);
192
193 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...
194 49
                $this->factory,
195 49
                $this->schemaContainer,
196 49
                $this->getContext(),
197 49
                $this->type,
198 49
                $name,
199 49
                $description,
200 49
                $nextLevel,
201 49
                $nextPathPrefix
202
            );
203
204
            [$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...
205 49
                $this->parseRelationshipLinks($this->schema, $this->data, $name, $description);
206
207 49
            $hasMeta = \array_key_exists(SchemaInterface::RELATIONSHIP_META, $description);
208 49
            $meta    = $hasMeta === true ? $description[SchemaInterface::RELATIONSHIP_META] : null;
209
210 49
            \assert(
211 49
                $hasData || $hasMeta || $hasLinks,
212 49
                "Relationship `$name` for type `" . $this->getType() .
213 49
                '` MUST contain at least one of the following: links, data or meta.'
214
            );
215
216 49
            $relationship = $this->factory->createRelationship(
217 49
                $nextPosition,
218 49
                $hasData,
219 49
                $relationshipData,
220 49
                $hasLinks,
221 49
                $links,
222 49
                $hasMeta,
223 49
                $meta
224
            );
225
226 49
            $this->relationshipsCache[$name] = $relationship;
227
228 49
            yield $name => $relationship;
229
        }
230 64
    }
231
232
    /**
233
     * @inheritdoc
234
     */
235 62
    public function hasLinks(): bool
236
    {
237 62
        $this->cacheLinks();
238
239 62
        return empty($this->links) === false;
240
    }
241
242
    /**
243
     * @inheritdoc
244
     */
245 61
    public function getLinks(): iterable
246
    {
247 61
        $this->cacheLinks();
248
249 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 249 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...
250
    }
251
252
    /**
253
     * @inheritdoc
254
     */
255 62
    public function hasResourceMeta(): bool
256
    {
257 62
        return $this->schema->hasResourceMeta($this->data);
258
    }
259
260
    /**
261
     * @inheritdoc
262
     */
263 1
    public function getResourceMeta()
264
    {
265 1
        return $this->schema->getResourceMeta($this->data);
266
    }
267
268
    /**
269
     * @return EditableContextInterface
270
     */
271 65
    protected function getContext(): EditableContextInterface
272
    {
273 65
        return $this->context;
274
    }
275
276
    /**
277
     * Read and parse links from schema.
278
     */
279 62
    private function cacheLinks(): void
280
    {
281 62
        if ($this->links === null) {
282 62
            $this->links = [];
283 62
            foreach ($this->schema->getLinks($this->data) as $name => $link) {
284 61
                \assert(\is_string($name) === true && empty($name) === false);
285 61
                \assert($link instanceof LinkInterface);
286 61
                $this->links[$name] = $link;
287
            }
288
        }
289 62
    }
290
291
    /**
292
     * @param string $name
293
     * @param array  $description
294
     *
295
     * @return bool
296
     */
297 49
    private function assertRelationshipNameAndDescription(string $name, array $description): bool
298
    {
299 49
        \assert(
300 49
            \is_string($name) === true && empty($name) === false,
301 49
            "Relationship names for type `" . $this->getType() . '` should be non-empty strings.'
302
        );
303 49
        \assert(
304 49
            \is_array($description) === true && empty($description) === false,
305 49
            "Relationship `$name` for type `" . $this->getType() . '` should be a non-empty array.'
306
        );
307
308 49
        return true;
309
    }
310
}
311