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

IdentifierAndResource::getAttributes()   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
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\ParserInterface;
23
use Neomerx\JsonApi\Contracts\Parser\PositionInterface;
24
use Neomerx\JsonApi\Contracts\Parser\ResourceInterface;
25
use Neomerx\JsonApi\Contracts\Schema\LinkInterface;
26
use Neomerx\JsonApi\Contracts\Schema\SchemaContainerInterface;
27
use Neomerx\JsonApi\Contracts\Schema\SchemaInterface;
28
use Neomerx\JsonApi\Parser\RelationshipData\ParseRelationshipDataTrait;
29
use Neomerx\JsonApi\Parser\RelationshipData\ParseRelationshipLinksTrait;
30
31
/**
32
 * @package Neomerx\JsonApi
33
 */
34
class IdentifierAndResource implements ResourceInterface
35
{
36
    use ParseRelationshipDataTrait, ParseRelationshipLinksTrait;
37
38
    /** @var string */
39
    public const MSG_NO_SCHEMA_FOUND = 'No Schema found for resource `%s` at path `%s`.';
40
41
    /** @var string */
42
    public const MSG_INVALID_OPERATION = 'Invalid operation.';
43
44
    /**
45
     * @var PositionInterface
46
     */
47
    private $position;
48
49
    /**
50
     * @var FactoryInterface
51
     */
52
    private $factory;
53
54
    /**
55
     * @var SchemaContainerInterface
56
     */
57
    private $schemaContainer;
58
59
    /**
60
     * @var SchemaInterface
61
     */
62
    private $schema;
63
64
    /**
65
     * @var mixed
66
     */
67
    private $data;
68
69
    /**
70
     * @var string
71
     */
72
    private $index;
73
74
    /**
75
     * @var string
76
     */
77
    private $type;
78
79
    /**
80
     * @var null|array
81
     */
82
    private $links = null;
83
84
    /**
85
     * @var null|array
86
     */
87
    private $relationshipsCache = null;
88
89
    /**
90
     * @param PositionInterface        $position
91
     * @param FactoryInterface         $factory
92
     * @param SchemaContainerInterface $container
93
     * @param mixed                    $data
94
     */
95 60
    public function __construct(
96
        PositionInterface $position,
97
        FactoryInterface $factory,
98
        SchemaContainerInterface $container,
99
        $data
100
    ) {
101 60
        $schema = $container->getSchema($data);
102
        $this
103 60
            ->setPosition($position)
104 60
            ->setFactory($factory)
105 60
            ->setSchemaContainer($container)
106 60
            ->setSchema($schema)
107 60
            ->setData($data);
108 60
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113 60
    public function getPosition(): PositionInterface
114
    {
115 60
        return $this->position;
116
    }
117
118
    /**
119
     * @inheritdoc
120
     */
121 60
    public function getId(): ?string
122
    {
123 60
        return $this->index;
124
    }
125
126
    /**
127
     * @inheritdoc
128
     */
129 60
    public function getType(): string
130
    {
131 60
        return $this->type;
132
    }
133
134
    /**
135
     * @inheritdoc
136
     */
137 31
    public function hasIdentifierMeta(): bool
138
    {
139 31
        return $this->getSchema()->hasIdentifierMeta($this->getData());
140
    }
141
142
    /**
143
     * @inheritdoc
144
     */
145 2
    public function getIdentifierMeta()
146
    {
147 2
        return $this->getSchema()->getIdentifierMeta($this->getData());
148
    }
149
150
    /**
151
     * @inheritdoc
152
     */
153 57
    public function getAttributes(): iterable
154
    {
155 57
        return $this->getSchema()->getAttributes($this->getData());
156
    }
157
158
    /**
159
     * @inheritdoc
160
     */
161 60
    public function getRelationships(): iterable
162
    {
163 60
        if ($this->relationshipsCache !== null) {
164 57
            yield from $this->relationshipsCache;
165
166 56
            return;
167
        }
168
169 60
        $this->relationshipsCache = [];
170
171 60
        $currentPath    = $this->position->getPath();
172 60
        $nextLevel      = $this->position->getLevel() + 1;
173 60
        $nextPathPrefix = empty($currentPath) === true ? '' : $currentPath . PositionInterface::PATH_SEPARATOR;
174 60
        foreach ($this->schema->getRelationships($this->data) as $name => $description) {
175 45
            assert($this->assertRelationshipNameAndDescription($name, $description) === true);
176
177 45
            [$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...
178 45
                $this->factory,
179 45
                $this->schemaContainer,
180 45
                $this->type,
181 45
                $name,
182 45
                $description,
183 45
                $nextLevel,
184 45
                $nextPathPrefix
185
            );
186
187
            [$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...
188 45
                $this->parseRelationshipLinks($this->schema, $this->data, $name, $description);
189
190 45
            $hasMeta = isset($description[SchemaInterface::RELATIONSHIP_META]);
191 45
            $meta    = $hasMeta === true ? $description[SchemaInterface::RELATIONSHIP_META] : null;
192
193 45
            assert(
194 45
                $hasData || $hasMeta || $hasLinks,
195 45
                "Relationship `{$name}` for type `{$this->type}` MUST contain at least one of the following: links, data or meta."
196
            );
197
198 45
            $relationship = $this->factory->createRelationship(
199 45
                $nextPosition,
200 45
                $hasData,
201 45
                $relationshipData,
202 45
                $hasLinks,
203 45
                $links,
204 45
                $hasMeta,
205 45
                $meta
206
            );
207
208 45
            $this->relationshipsCache[$name] = $relationship;
209
210 45
            yield $name => $relationship;
211
        }
212 60
    }
213
214
    /**
215
     * @inheritdoc
216
     */
217 57
    public function hasLinks(): bool
218
    {
219 57
        $this->cacheLinks();
220
221 57
        return empty($this->links) === false;
222
    }
223
224
    /**
225
     * @inheritdoc
226
     */
227 56
    public function getLinks(): iterable
228
    {
229 56
        $this->cacheLinks();
230
231 56
        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 231 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...
232
    }
233
234
    /**
235
     * @inheritdoc
236
     */
237 57
    public function hasResourceMeta(): bool
238
    {
239 57
        return $this->getSchema()->hasResourceMeta($this->getData());
240
    }
241
242
    /**
243
     * @inheritdoc
244
     */
245 1
    public function getResourceMeta()
246
    {
247 1
        return $this->getSchema()->getResourceMeta($this->getData());
248
    }
249
250
    /**
251
     * @inheritdoc
252
     */
253 60
    protected function setPosition(PositionInterface $position): self
254
    {
255 60
        assert($position->getLevel() >= ParserInterface::ROOT_LEVEL);
256
257 60
        $this->position = $position;
258
259 60
        return $this;
260
    }
261
262
    /**
263
     * @return FactoryInterface
264
     */
265
    protected function getFactory(): FactoryInterface
266
    {
267
        return $this->factory;
268
    }
269
270
    /**
271
     * @param FactoryInterface $factory
272
     *
273
     * @return self
274
     */
275 60
    protected function setFactory(FactoryInterface $factory): self
276
    {
277 60
        $this->factory = $factory;
278
279 60
        return $this;
280
    }
281
282
    /**
283
     * @return SchemaContainerInterface
284
     */
285
    protected function getSchemaContainer(): SchemaContainerInterface
286
    {
287
        return $this->schemaContainer;
288
    }
289
290
    /**
291
     * @param SchemaContainerInterface $container
292
     *
293
     * @return self
294
     */
295 60
    protected function setSchemaContainer(SchemaContainerInterface $container): self
296
    {
297 60
        $this->schemaContainer = $container;
298
299 60
        return $this;
300
    }
301
302
    /**
303
     * @return SchemaInterface
304
     */
305 60
    protected function getSchema(): SchemaInterface
306
    {
307 60
        return $this->schema;
308
    }
309
310
    /**
311
     * @param SchemaInterface $schema
312
     *
313
     * @return self
314
     */
315 60
    protected function setSchema(SchemaInterface $schema): self
316
    {
317 60
        $this->schema = $schema;
318
319 60
        return $this;
320
    }
321
322
    /**
323
     * @return mixed
324
     */
325 60
    protected function getData()
326
    {
327 60
        return $this->data;
328
    }
329
330
    /**
331
     * @param mixed $data
332
     *
333
     * @return self
334
     */
335 60
    protected function setData($data): self
336
    {
337 60
        $this->data  = $data;
338 60
        $this->index = $this->getSchema()->getId($data);
339 60
        $this->type  = $this->getSchema()->getType();
340
341 60
        return $this;
342
    }
343
344
    /**
345
     * Read and parse links from schema.
346
     */
347 57
    private function cacheLinks(): void
348
    {
349 57
        if ($this->links === null) {
350 57
            $this->links = [];
351 57
            foreach ($this->getSchema()->getLinks($this->getData()) as $name => $link) {
352 56
                assert(is_string($name) === true && empty($name) === false);
353 56
                assert($link instanceof LinkInterface);
354 56
                $this->links[$name] = $link;
355
            }
356
        }
357 57
    }
358
359
    /**
360
     * @param string $name
361
     * @param array  $description
362
     *
363
     * @return bool
364
     */
365 45
    private function assertRelationshipNameAndDescription(string $name, array $description): bool
366
    {
367 45
        assert(
368 45
            is_string($name) === true && empty($name) === false,
369 45
            "Relationship names for type `{$this->type}` should be non-empty strings."
370
        );
371 45
        assert(
372 45
            is_array($description) === true && empty($description) === false,
373 45
            "Relationship `{$name}` for type `{$this->type}` should be a non-empty array."
374
        );
375
376 45
        return true;
377
    }
378
}
379