Completed
Branch next (8c611a)
by Neomerx
02:36
created

IdentifierAndResource::hasLinks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
ccs 3
cts 3
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 59
    public function __construct(
96
        PositionInterface $position,
97
        FactoryInterface $factory,
98
        SchemaContainerInterface $container,
99
        $data
100
    ) {
101 59
        $schema = $container->getSchema($data);
102
        $this
103 59
            ->setPosition($position)
104 59
            ->setFactory($factory)
105 59
            ->setSchemaContainer($container)
106 59
            ->setSchema($schema)
107 59
            ->setData($data);
108 59
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113 59
    public function getPosition(): PositionInterface
114
    {
115 59
        return $this->position;
116
    }
117
118
    /**
119
     * @inheritdoc
120
     */
121 59
    public function getId(): ?string
122
    {
123 59
        return $this->index;
124
    }
125
126
    /**
127
     * @inheritdoc
128
     */
129 59
    public function getType(): string
130
    {
131 59
        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 56
    public function getAttributes(): iterable
154
    {
155 56
        return $this->getSchema()->getAttributes($this->getData());
156
    }
157
158
    /**
159
     * @inheritdoc
160
     */
161 59
    public function getRelationships(): iterable
162
    {
163 59
        if ($this->relationshipsCache !== null) {
164 56
            yield from $this->relationshipsCache;
165
166 55
            return;
167
        }
168
169 59
        $this->relationshipsCache = [];
170
171 59
        $currentPath    = $this->getPosition()->getPath();
172 59
        $nextLevel      = $this->getPosition()->getLevel() + 1;
173 59
        $nextPathPrefix = empty($currentPath) === true ? '' : $currentPath . PositionInterface::PATH_SEPARATOR;
174 59
        foreach ($this->getSchema()->getRelationships($this->getData()) as $name => $description) {
175 44
            assert($this->assertRelationshipNameAndDescription($name, $description) === true);
176
177 44
            [$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 44
                $this->getFactory(),
179 44
                $this->getSchemaContainer(),
180 44
                $this->getType(),
181 44
                $name,
182 44
                $description,
183 44
                $nextLevel,
184 44
                $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 44
                $this->parseRelationshipLinks($this->getSchema(), $this->getData(), $name, $description);
189
190 44
            $hasMeta = array_key_exists(SchemaInterface::RELATIONSHIP_META, $description);
191 44
            $meta    = $hasMeta === true ? $description[SchemaInterface::RELATIONSHIP_META] : null;
192
193 44
            assert(
194 44
                $hasData || $hasMeta || $hasLinks,
195 44
                "Relationship `$name` for type `" . $this->getType() .
196 44
                '` MUST contain at least one of the following: links, data or meta.'
197
            );
198
199 44
            $relationship = $this->getFactory()->createRelationship(
200 44
                $nextPosition,
201 44
                $hasData,
202 44
                $relationshipData,
203 44
                $hasLinks,
204 44
                $links,
205 44
                $hasMeta,
206 44
                $meta
207
            );
208
209 44
            $this->relationshipsCache[$name] = $relationship;
210
211 44
            yield $name => $relationship;
212
        }
213 59
    }
214
215
    /**
216
     * @inheritdoc
217
     */
218 56
    public function hasLinks(): bool
219
    {
220 56
        $this->cacheLinks();
221
222 56
        return empty($this->links) === false;
223
    }
224
225
    /**
226
     * @inheritdoc
227
     */
228 55
    public function getLinks(): iterable
229
    {
230 55
        $this->cacheLinks();
231
232 55
        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 232 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...
233
    }
234
235
    /**
236
     * @inheritdoc
237
     */
238 56
    public function hasResourceMeta(): bool
239
    {
240 56
        return $this->getSchema()->hasResourceMeta($this->getData());
241
    }
242
243
    /**
244
     * @inheritdoc
245
     */
246 1
    public function getResourceMeta()
247
    {
248 1
        return $this->getSchema()->getResourceMeta($this->getData());
249
    }
250
251
    /**
252
     * @inheritdoc
253
     */
254 59
    protected function setPosition(PositionInterface $position): self
255
    {
256 59
        assert($position->getLevel() >= ParserInterface::ROOT_LEVEL);
257
258 59
        $this->position = $position;
259
260 59
        return $this;
261
    }
262
263
    /**
264
     * @return FactoryInterface
265
     */
266 44
    protected function getFactory(): FactoryInterface
267
    {
268 44
        return $this->factory;
269
    }
270
271
    /**
272
     * @param FactoryInterface $factory
273
     *
274
     * @return self
275
     */
276 59
    protected function setFactory(FactoryInterface $factory): self
277
    {
278 59
        $this->factory = $factory;
279
280 59
        return $this;
281
    }
282
283
    /**
284
     * @return SchemaContainerInterface
285
     */
286 44
    protected function getSchemaContainer(): SchemaContainerInterface
287
    {
288 44
        return $this->schemaContainer;
289
    }
290
291
    /**
292
     * @param SchemaContainerInterface $container
293
     *
294
     * @return self
295
     */
296 59
    protected function setSchemaContainer(SchemaContainerInterface $container): self
297
    {
298 59
        $this->schemaContainer = $container;
299
300 59
        return $this;
301
    }
302
303
    /**
304
     * @return SchemaInterface
305
     */
306 59
    protected function getSchema(): SchemaInterface
307
    {
308 59
        return $this->schema;
309
    }
310
311
    /**
312
     * @param SchemaInterface $schema
313
     *
314
     * @return self
315
     */
316 59
    protected function setSchema(SchemaInterface $schema): self
317
    {
318 59
        $this->schema = $schema;
319
320 59
        return $this;
321
    }
322
323
    /**
324
     * @return mixed
325
     */
326 59
    protected function getData()
327
    {
328 59
        return $this->data;
329
    }
330
331
    /**
332
     * @param mixed $data
333
     *
334
     * @return self
335
     */
336 59
    protected function setData($data): self
337
    {
338 59
        $this->data  = $data;
339 59
        $this->index = $this->getSchema()->getId($data);
340 59
        $this->type  = $this->getSchema()->getType();
341
342 59
        return $this;
343
    }
344
345
    /**
346
     * Read and parse links from schema.
347
     */
348 56
    private function cacheLinks(): void
349
    {
350 56
        if ($this->links === null) {
351 56
            $this->links = [];
352 56
            foreach ($this->getSchema()->getLinks($this->getData()) as $name => $link) {
353 55
                assert(is_string($name) === true && empty($name) === false);
354 55
                assert($link instanceof LinkInterface);
355 55
                $this->links[$name] = $link;
356
            }
357
        }
358 56
    }
359
360
    /**
361
     * @param string $name
362
     * @param array  $description
363
     *
364
     * @return bool
365
     */
366 44
    private function assertRelationshipNameAndDescription(string $name, array $description): bool
367
    {
368 44
        assert(
369 44
            is_string($name) === true && empty($name) === false,
370 44
            "Relationship names for type `" . $this->getType() . '` should be non-empty strings.'
371
        );
372 44
        assert(
373 44
            is_array($description) === true && empty($description) === false,
374 44
            "Relationship `$name` for type `" . $this->getType() . '` should be a non-empty array.'
375
        );
376
377 44
        return true;
378
    }
379
}
380