Skip to content

Commit c3b505c

Browse files
committed
More work on spec validation
1 parent 4e33073 commit c3b505c

20 files changed

+959
-234
lines changed

resources/lang/en/errors.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@
6464
'code' => '',
6565
],
6666

67+
'member_array_expected' => [
68+
'title' => 'Non-Compliant JSON API Document',
69+
'detail' => 'The member :member must be an array.',
70+
'code' => '',
71+
],
72+
6773
'member_identifier_expected' => [
6874
'title' => 'Non-Compliant JSON API Document',
6975
'detail' => 'The member :member must be a resource identifier.',

resources/lang/nl/errors.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@
6464
'code' => '',
6565
],
6666

67+
'member_array_expected' => [
68+
'title' => 'Non-Compliant JSON API Document',
69+
'detail' => 'Het onderdeel :member moet een array zijn.',
70+
'code' => '',
71+
],
72+
6773
'member_identifier_expected' => [
6874
'title' => 'Niet-Conform JSON API Document',
6975
'detail' => 'Het onderdeel :member moet een resource identifier zijn.',

src/Core/Document/ErrorList.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use IteratorAggregate;
2424
use LaravelJsonApi\Contracts\Serializable;
2525
use LogicException;
26+
use function array_merge;
2627
use function collect;
2728

2829
class ErrorList implements Serializable, Countable, IteratorAggregate
@@ -97,6 +98,22 @@ public function push(Error ...$errors): self
9798
return $this;
9899
}
99100

101+
/**
102+
* Merge errors.
103+
*
104+
* @param iterable $errors
105+
* @return $this
106+
*/
107+
public function merge(iterable $errors): self
108+
{
109+
if ($errors instanceof static) {
110+
$this->stack = array_merge($this->stack, $errors->stack);
111+
return $this;
112+
}
113+
114+
return $this->push(...$errors);
115+
}
116+
100117
/**
101118
* @return bool
102119
*/

src/Spec/Builder.php

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,11 @@
2020
namespace LaravelJsonApi\Spec;
2121

2222
use Illuminate\Pipeline\Pipeline;
23+
use function json_decode;
2324

2425
class Builder
2526
{
2627

27-
/**
28-
* @var Translator
29-
*/
30-
private Translator $translator;
31-
3228
/**
3329
* @var Pipeline
3430
*/
@@ -39,20 +35,13 @@ class Builder
3935
*/
4036
private ?string $expects = null;
4137

42-
/**
43-
* @var bool
44-
*/
45-
private bool $clientIds = false;
46-
4738
/**
4839
* Builder constructor.
4940
*
50-
* @param Translator $translator
5141
* @param Pipeline $pipeline
5242
*/
53-
public function __construct(Translator $translator, Pipeline $pipeline)
43+
public function __construct(Pipeline $pipeline)
5444
{
55-
$this->translator = $translator;
5645
$this->pipeline = $pipeline;
5746
}
5847

@@ -70,36 +59,31 @@ public function expects(string $resourceType): self
7059
}
7160

7261
/**
73-
* Set whether client ids are allowed.
74-
*
75-
* @param bool $supported
76-
* @return $this
77-
*/
78-
public function clientIds(bool $supported = true): self
79-
{
80-
$this->clientIds = $supported;
81-
82-
return $this;
83-
}
84-
85-
/**
86-
* @param $json
62+
* @param string|object $json
8763
* @return Document
8864
*/
8965
public function build($json): Document
9066
{
67+
if (is_string($json)) {
68+
$json = json_decode($json, false, 512, JSON_THROW_ON_ERROR);
69+
}
70+
71+
if (!is_object($json)) {
72+
throw new \InvalidArgumentException('Expecting a string or object.');
73+
}
74+
9175
$pipes = [
92-
new Validators\DataValidator($this->translator),
93-
new Validators\TypeValidator($this->translator, $this->expects),
94-
new Validators\ClientIdValidator($this->translator, $this->expects, $this->clientIds),
95-
new Validators\AttributesValidator($this->translator),
96-
new Validators\RelationshipsValidator($this->translator),
97-
new Validators\RelationshipValidator($this->translator),
98-
new Validators\FieldsValidator($this->translator),
76+
Validators\DataValidator::class,
77+
Validators\TypeValidator::class,
78+
Validators\ClientIdValidator::class,
79+
Validators\FieldsValidator::class,
80+
Validators\AttributesValidator::class,
81+
Validators\RelationshipsValidator::class,
82+
Validators\RelationshipValidator::class,
9983
];
10084

10185
return $this->pipeline
102-
->send(Document::cast($json))
86+
->send(new Document($json, $this->expects))
10387
->through($pipes)
10488
->via('validate')
10589
->thenReturn();

src/Spec/Document.php

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
namespace LaravelJsonApi\Spec;
2121

2222
use LaravelJsonApi\Core\Document\ErrorList;
23-
use function json_decode;
2423

2524
class Document
2625
{
@@ -31,46 +30,32 @@ class Document
3130
private object $document;
3231

3332
/**
34-
* @var ErrorList
33+
* @var string
3534
*/
36-
private ErrorList $errors;
35+
private string $resourceType;
3736

3837
/**
39-
* @param $json
40-
* @return static
38+
* @var string|null
4139
*/
42-
public static function cast($json): self
43-
{
44-
if (is_string($json)) {
45-
return self::fromString($json);
46-
}
47-
48-
if (is_object($json)) {
49-
return new self($json);
50-
}
51-
52-
throw new \UnexpectedValueException('Expecting a string or decoded JSON object.');
53-
}
40+
private ?string $relation;
5441

5542
/**
56-
* Create a document from a string.
57-
*
58-
* @param string $json
59-
* @return Document
43+
* @var ErrorList
6044
*/
61-
public static function fromString(string $json): self
62-
{
63-
return new self(json_decode($json, false, JSON_THROW_ON_ERROR));
64-
}
45+
private ErrorList $errors;
6546

6647
/**
6748
* Document constructor.
6849
*
6950
* @param object $document
51+
* @param string $resourceType
52+
* @param string|null $relation
7053
*/
71-
public function __construct(object $document)
54+
public function __construct(object $document, string $resourceType, string $relation = null)
7255
{
7356
$this->document = $document;
57+
$this->resourceType = $resourceType;
58+
$this->relation = $relation;
7459
$this->errors = new ErrorList();
7560
}
7661

@@ -93,7 +78,43 @@ public function __get($name)
9378
}
9479

9580
/**
96-
* Get a value.
81+
* Get the document's expected resource type.
82+
*
83+
* @return string
84+
*/
85+
public function type(): string
86+
{
87+
return $this->resourceType;
88+
}
89+
90+
/**
91+
* Get the relation that the document represents.
92+
*
93+
* @return string|null
94+
*/
95+
public function relation(): ?string
96+
{
97+
return $this->relation;
98+
}
99+
100+
/**
101+
* @return bool
102+
*/
103+
public function isRelation(): bool
104+
{
105+
return is_string($this->relation);
106+
}
107+
108+
/**
109+
* @return bool
110+
*/
111+
public function isNotRelation(): bool
112+
{
113+
return !$this->isRelation();
114+
}
115+
116+
/**
117+
* Get a value from the document using dot notation.
97118
*
98119
* @param string $path
99120
* @param mixed|null $default

src/Spec/Factory.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Spec;
21+
22+
use LaravelJsonApi\Spec\Values\Identifier;
23+
use LaravelJsonApi\Spec\Values\ToMany;
24+
use LaravelJsonApi\Spec\Values\ToOne;
25+
26+
class Factory
27+
{
28+
29+
/**
30+
* @var Specification
31+
*/
32+
private Specification $spec;
33+
34+
/**
35+
* @var Translator
36+
*/
37+
private Translator $translator;
38+
39+
/**
40+
* Factory constructor.
41+
*
42+
* @param Specification $spec
43+
* @param Translator $translator
44+
*/
45+
public function __construct(Specification $spec, Translator $translator)
46+
{
47+
$this->spec = $spec;
48+
$this->translator = $translator;
49+
}
50+
51+
52+
/**
53+
* Create a resource identifier object.
54+
*
55+
* @param string $path
56+
* @param $value
57+
* @return Identifier
58+
*/
59+
public function createIdentifierValue(string $path, $value): Identifier
60+
{
61+
return new Identifier($this->spec, $this->translator, $path, $value);
62+
}
63+
64+
/**
65+
* Create a to-one relationship object.
66+
*
67+
* @param string $path
68+
* @param $value
69+
* @return ToOne
70+
*/
71+
public function createToOneValue(string $path, $value): ToOne
72+
{
73+
return new ToOne($this->translator, $this, $path, $value);
74+
}
75+
76+
/**
77+
* Create a to-many relationship object.
78+
*
79+
* @param string $path
80+
* @param $value
81+
* @return ToMany
82+
*/
83+
public function createToManyValue(string $path, $value): ToMany
84+
{
85+
return new ToMany($this->translator, $this, $path, $value);
86+
}
87+
}

0 commit comments

Comments
 (0)