Skip to content

Commit 7d8ade2

Browse files
committed
initial work on updating fields for validation
1 parent 4c9f506 commit 7d8ade2

17 files changed

+706
-21
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"ext-json": "*",
2828
"illuminate/database": "^10.0",
2929
"illuminate/support": "^10.0",
30-
"laravel-json-api/core": "^4.0"
30+
"laravel-json-api/core": "^4.0",
31+
"laravel-json-api/validation": "^4.0"
3132
},
3233
"require-dev": {
3334
"orchestra/testbench": "^8.0",

src/Fields/Boolean.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@
1919

2020
namespace LaravelJsonApi\Eloquent\Fields;
2121

22-
class Boolean extends Attribute
22+
use LaravelJsonApi\Validation\Fields\IsValidated;
23+
use LaravelJsonApi\Validation\Fields\ValidatedWithListOfRules;
24+
use LaravelJsonApi\Validation\Rules\JsonBoolean;
25+
26+
class Boolean extends Attribute implements IsValidated
2327
{
28+
use ValidatedWithListOfRules;
2429

2530
/**
2631
* Create a boolean attribute.
@@ -34,12 +39,20 @@ public static function make(string $fieldName, string $column = null): self
3439
return new self($fieldName, $column);
3540
}
3641

42+
/**
43+
* @return array
44+
*/
45+
protected function defaultRules(): array
46+
{
47+
return [new JsonBoolean()];
48+
}
49+
3750
/**
3851
* @inheritDoc
3952
*/
4053
protected function assertValue($value): void
4154
{
42-
if (!is_null($value) && !is_bool($value)) {
55+
if ($value !== null && !is_bool($value)) {
4356
throw new \UnexpectedValueException(sprintf(
4457
'Expecting the value of attribute %s to be a boolean.',
4558
$this->name()

src/Fields/Concerns/IsReadOnly.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,21 @@
2525

2626
trait IsReadOnly
2727
{
28-
2928
/**
3029
* Whether the field is read-only.
3130
*
3231
* @var Closure|bool
3332
*/
34-
private $readOnly = false;
33+
private Closure|bool $readOnly = false;
3534

3635
/**
3736
* Mark the field as read-only.
3837
*
3938
* @param Closure|bool $callback
4039
* @return $this
4140
*/
42-
public function readOnly($callback = true): self
41+
public function readOnly(Closure|bool $callback = true): static
4342
{
44-
if (!is_bool($callback) && !$callback instanceof Closure) {
45-
throw new InvalidArgumentException('Expecting a boolean or closure.');
46-
}
47-
4843
$this->readOnly = $callback;
4944

5045
return $this;
@@ -55,7 +50,7 @@ public function readOnly($callback = true): self
5550
*
5651
* @return $this
5752
*/
58-
public function readOnlyOnCreate(): self
53+
public function readOnlyOnCreate(): static
5954
{
6055
$this->readOnly(static fn($request) => $request && $request->isMethod('POST'));
6156

@@ -67,7 +62,7 @@ public function readOnlyOnCreate(): self
6762
*
6863
* @return $this
6964
*/
70-
public function readOnlyOnUpdate(): self
65+
public function readOnlyOnUpdate(): static
7166
{
7267
$this->readOnly(static fn($request) => $request && $request->isMethod('PATCH'));
7368

src/Fields/DateTime.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@
2020
namespace LaravelJsonApi\Eloquent\Fields;
2121

2222
use Carbon\CarbonInterface;
23+
use Closure;
2324
use Illuminate\Support\Facades\Date;
25+
use LaravelJsonApi\Validation\Fields\IsValidated;
26+
use LaravelJsonApi\Validation\Fields\ValidatedWithListOfRules;
27+
use LaravelJsonApi\Validation\Rules\DateTimeIso8601;
2428
use function config;
2529

26-
class DateTime extends Attribute
30+
class DateTime extends Attribute implements IsValidated
2731
{
32+
use ValidatedWithListOfRules;
2833

2934
/**
3035
* Should dates be converted to the defined time zone?
@@ -121,6 +126,14 @@ protected function parse($value): ?CarbonInterface
121126
return $value;
122127
}
123128

129+
/**
130+
* @return DateTimeIso8601[]
131+
*/
132+
protected function defaultRules(): array
133+
{
134+
return [new DateTimeIso8601()];
135+
}
136+
124137
/**
125138
* @inheritDoc
126139
*/

src/Fields/ID.php

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020
namespace LaravelJsonApi\Eloquent\Fields;
2121

2222
use Illuminate\Database\Eloquent\Model;
23+
use Illuminate\Http\Request;
2324
use LaravelJsonApi\Contracts\Schema\ID as IDContract;
2425
use LaravelJsonApi\Core\Schema\Concerns\ClientIds;
2526
use LaravelJsonApi\Core\Schema\Concerns\MatchesIds;
2627
use LaravelJsonApi\Core\Schema\Concerns\Sortable;
2728
use LaravelJsonApi\Eloquent\Contracts\Fillable;
29+
use LaravelJsonApi\Validation\Fields\IsValidated;
2830

29-
class ID implements IDContract, Fillable
31+
class ID implements IDContract, Fillable, IsValidated
3032
{
31-
3233
use ClientIds;
3334
use MatchesIds;
3435
use Sortable;
@@ -38,6 +39,11 @@ class ID implements IDContract, Fillable
3839
*/
3940
private ?string $column;
4041

42+
/**
43+
* @var string
44+
*/
45+
private string $validationModifier = 'required';
46+
4147
/**
4248
* Create an id field.
4349
*
@@ -112,6 +118,36 @@ public function isNotReadOnly($request): bool
112118
return !$this->isReadOnly($request);
113119
}
114120

121+
/**
122+
* @return $this
123+
*/
124+
public function nullable(): self
125+
{
126+
$this->validationModifier = 'nullable';
127+
128+
return $this;
129+
}
130+
131+
/**
132+
* @inheritDoc
133+
*/
134+
public function rulesForCreation(?Request $request): array|null
135+
{
136+
if ($this->acceptsClientIds()) {
137+
return [$this->validationModifier, "regex:/^{$this->pattern}$/{$this->flags}"];
138+
}
139+
140+
return null;
141+
}
142+
143+
/**
144+
* @inheritDoc
145+
*/
146+
public function rulesForUpdate(?Request $request, object $model): ?array
147+
{
148+
return null;
149+
}
150+
115151
/**
116152
* @inheritDoc
117153
*/

src/Fields/Integer.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
/*
3+
* Copyright 2023 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\Eloquent\Fields;
21+
22+
use LaravelJsonApi\Validation\Fields\IsValidated;
23+
use LaravelJsonApi\Validation\Fields\ValidatedWithListOfRules;
24+
use LaravelJsonApi\Validation\Rules\JsonNumber;
25+
use UnexpectedValueException;
26+
27+
class Integer extends Attribute implements IsValidated
28+
{
29+
use ValidatedWithListOfRules;
30+
31+
/**
32+
* @var bool
33+
*/
34+
private bool $acceptStrings = false;
35+
36+
/**
37+
* Create a number attribute.
38+
*
39+
* @param string $fieldName
40+
* @param string|null $column
41+
* @return self
42+
*/
43+
public static function make(string $fieldName, string $column = null): self
44+
{
45+
return new self($fieldName, $column);
46+
}
47+
48+
/**
49+
* @return $this
50+
*/
51+
public function acceptStrings(): self
52+
{
53+
$this->acceptStrings = true;
54+
55+
return $this;
56+
}
57+
58+
/**
59+
* @return array
60+
*/
61+
protected function defaultRules(): array
62+
{
63+
if ($this->acceptStrings) {
64+
return ['numeric', 'integer'];
65+
}
66+
67+
return [(new JsonNumber())->onlyIntegers()];
68+
}
69+
70+
/**
71+
* @inheritDoc
72+
*/
73+
protected function assertValue($value): void
74+
{
75+
if (!$this->isInt($value)) {
76+
$expected = $this->acceptStrings ?
77+
'an integer or a numeric string that is an integer.' :
78+
'an integer.';
79+
80+
throw new UnexpectedValueException(sprintf(
81+
'Expecting the value of attribute %s to be ' . $expected,
82+
$this->name(),
83+
));
84+
}
85+
}
86+
87+
/**
88+
* Is the value a numeric value that this field accepts?
89+
*
90+
* @param mixed $value
91+
* @return bool
92+
*/
93+
private function isInt(mixed $value): bool
94+
{
95+
if ($this->acceptStrings && is_string($value) && is_numeric($value)) {
96+
$value = filter_var($value, FILTER_VALIDATE_INT);
97+
}
98+
99+
if ($value === null || is_int($value)) {
100+
return true;
101+
}
102+
103+
return false;
104+
}
105+
}

src/Fields/Number.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@
1919

2020
namespace LaravelJsonApi\Eloquent\Fields;
2121

22+
use LaravelJsonApi\Validation\Fields\IsValidated;
23+
use LaravelJsonApi\Validation\Fields\ValidatedWithListOfRules;
24+
use LaravelJsonApi\Validation\Rules\JsonNumber;
2225
use UnexpectedValueException;
2326

24-
class Number extends Attribute
27+
class Number extends Attribute implements IsValidated
2528
{
29+
use ValidatedWithListOfRules;
30+
2631
/**
2732
* @var bool
2833
*/
@@ -50,6 +55,18 @@ public function acceptStrings(): self
5055
return $this;
5156
}
5257

58+
/**
59+
* @return array
60+
*/
61+
protected function defaultRules(): array
62+
{
63+
if ($this->acceptStrings) {
64+
return ['numeric'];
65+
}
66+
67+
return [new JsonNumber()];
68+
}
69+
5370
/**
5471
* @inheritDoc
5572
*/

src/Fields/SoftDelete.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
namespace LaravelJsonApi\Eloquent\Fields;
2121

2222
use Illuminate\Support\Facades\Date;
23+
use LaravelJsonApi\Validation\Rules\DateTimeIso8601;
24+
use LaravelJsonApi\Validation\Rules\JsonBoolean;
2325
use UnexpectedValueException;
2426
use function boolval;
2527
use function is_bool;
@@ -28,7 +30,6 @@
2830

2931
class SoftDelete extends DateTime
3032
{
31-
3233
/**
3334
* @var bool
3435
*/
@@ -68,12 +69,23 @@ public function serialize(object $model)
6869
return parent::serialize($model);
6970
}
7071

72+
/**
73+
* @return array
74+
*/
75+
protected function defaultRules(): array
76+
{
77+
return [
78+
'nullable',
79+
$this->boolean ? new JsonBoolean() : new DateTimeIso8601(),
80+
];
81+
}
82+
7183
/**
7284
* @inheritDoc
7385
*/
7486
protected function deserialize($value)
7587
{
76-
if (true === $this->boolean && (is_bool($value) || is_null($value))) {
88+
if (true === $this->boolean && (is_bool($value) || $value === null)) {
7789
return $this->parse($value ? Date::now() : null);
7890
}
7991

0 commit comments

Comments
 (0)