generated from spatie/package-skeleton-laravel
-
-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
83c46bf
commit 575c40d
Showing
16 changed files
with
467 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\DataPipes; | ||
|
||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\Validator; | ||
use Spatie\LaravelData\Rules\InitialPropertyRule; | ||
use Spatie\LaravelData\Support\DataClass; | ||
|
||
class InitialValidationDataPipe implements DataPipe | ||
{ | ||
public function handle(mixed $payload, DataClass $class, Collection $properties): Collection | ||
{ | ||
$rules = []; | ||
|
||
foreach ($class->properties as $property) { | ||
$rules[$property->name] = new InitialPropertyRule($property); | ||
} | ||
|
||
Validator::make($properties->toArray(), $rules)->validate(); | ||
|
||
return $properties; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Rules; | ||
|
||
use Closure; | ||
use Exception; | ||
use Illuminate\Contracts\Validation\ValidationRule; | ||
use Illuminate\Support\Facades\Validator; | ||
use Spatie\LaravelData\Support\DataProperty; | ||
use Spatie\LaravelData\Support\Types\MultiType; | ||
use Spatie\LaravelData\Support\Types\PartialType; | ||
use Spatie\LaravelData\Support\Types\SingleType; | ||
use Spatie\LaravelData\Support\Types\UndefinedType; | ||
use Stringable; | ||
|
||
class InitialPropertyRule implements ValidationRule | ||
{ | ||
public function __construct( | ||
protected DataProperty $property | ||
) { | ||
} | ||
|
||
public function validate(string $attribute, mixed $value, Closure $fail): void | ||
{ | ||
$dataType = $this->property->type; | ||
$type = $dataType->type; | ||
|
||
if ($dataType->isMixed() || $type instanceof UndefinedType) { | ||
return; | ||
} | ||
|
||
if (! $dataType->isNullable() && $value === null) { | ||
$fail(__('validation.required', ['attribute' => $attribute])); | ||
} | ||
|
||
if ($dataType->isNullable() && $value === null) { | ||
return; | ||
} | ||
|
||
if ($dataType->type instanceof SingleType) { | ||
$this->validateSingleType($dataType->type, $attribute, $value, $fail); | ||
|
||
return; | ||
} | ||
|
||
if ($dataType->type instanceof MultiType) { | ||
$this->validateMultiType($dataType->type, $attribute, $value, $fail); | ||
|
||
return; | ||
} | ||
|
||
throw new Exception('Unexpected path, we should never get here'); | ||
} | ||
|
||
protected function validateSingleType( | ||
SingleType $singleType, | ||
string $attribute, | ||
mixed $value, | ||
Closure $fail, | ||
): void { | ||
$validity = $this->isPartialTypeValid($singleType->type, $attribute, $value); | ||
|
||
if ($validity === true) { | ||
return; | ||
} | ||
|
||
$fail($validity); | ||
} | ||
|
||
protected function validateMultiType( | ||
MultiType $multiType, | ||
string $attribute, | ||
mixed $value, | ||
Closure $fail, | ||
): void { | ||
$invalidMessages = []; | ||
|
||
foreach ($multiType->types as $type) { | ||
$validity = $this->isPartialTypeValid($type, $attribute, $value); | ||
|
||
if ($validity === true) { | ||
return; | ||
} | ||
|
||
$invalidMessages[] = $validity; | ||
} | ||
|
||
$fail(implode(' or ', $invalidMessages)); | ||
} | ||
|
||
protected function isPartialTypeValid( | ||
Check failure on line 91 in src/Rules/InitialPropertyRule.php GitHub Actions / P8.1 - L9.* - prefer-lowest - ubuntu-latest
Check failure on line 91 in src/Rules/InitialPropertyRule.php GitHub Actions / P8.1 - L9.* - prefer-stable - ubuntu-latest
|
||
PartialType $type, | ||
string $attribute, | ||
mixed $value | ||
): true|string { | ||
// TODO: casts always have precedence over here | ||
if ($this->property->cast) { | ||
return Validator::make( | ||
[$attribute => $value], | ||
[$attribute => $this->property->cast->preValidationRules($this->property, $value)] | ||
)->validate(); | ||
} | ||
|
||
if ($type->name === 'string' && ! $this->isValidString($value)) { | ||
return __('validation.string', ['attribute' => $attribute]); | ||
} | ||
|
||
if ($type->name === 'bool' && ! $this->isValidBool($value)) { | ||
return __('validation.boolean', ['attribute' => $attribute]); | ||
} | ||
|
||
if ($type->name === 'int' && ! $this->isValidIntOrFloat($value)) { | ||
return __('validation.integer', ['attribute' => $attribute]); | ||
} | ||
|
||
if ($type->name === 'float' && ! $this->isValidIntOrFloat($value)) { | ||
return __('validation.float', ['attribute' => $attribute]); | ||
} | ||
|
||
if ($type->name === 'array' && ! $this->isValidArray($value)) { | ||
return __('validation.array', ['attribute' => $attribute]); | ||
} | ||
|
||
// We're having an object over here, this should basically be catched by the casts | ||
// Unless it is a data object | ||
// Data objects can be created using magic methods, so we need check these types and count them all up | ||
// Data objects also can be created using from which accepts all values based upon their normalizer | ||
// Then there are datacollectables, these we can also automatically create | ||
// Again magic collect methods which can take any type | ||
// + some default types | ||
// For each item in such a collection we need to, again check the data objects | ||
|
||
return true; | ||
} | ||
|
||
protected function isValidString(mixed $value): bool | ||
{ | ||
return is_scalar($value) || $value instanceof Stringable; | ||
} | ||
|
||
protected function isValidBool(mixed $value): bool | ||
{ | ||
return is_scalar($value); | ||
} | ||
|
||
protected function isValidIntOrFloat(mixed $value): bool | ||
{ | ||
return is_numeric($value) || is_bool($value); | ||
} | ||
|
||
protected function isValidArray(mixed $value): bool | ||
{ | ||
return is_array($value); // TODO what if we have an arrayble? A) cast before validation, B) split casting and decide a cast before validation | ||
// Follow up -> add a new pipe, auto infer casts, which automatically adds global casts | ||
// We'll add support for a collection global cast from array which solves our problem | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace Spatie\LaravelData\Support; | ||
|
||
use Spatie\LaravelData\Casts\Cast; | ||
|
||
class TransientProperty | ||
{ | ||
public function __construct( | ||
public mixed $value, | ||
public DataProperty $property, | ||
public ?Cast $cast = null, // Pre determined cast -> see idea to set this in stone on the data property | ||
public bool $isExactValue = false, // Whether an object is exaxctly what the property needed, | ||
public bool $noCastFound = false, // Whether no cast was found for the property | ||
public ?DataMethod $magicMethod = null, // A determined magic method that can be used to create the value | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
use Spatie\LaravelData\Data; | ||
|
||
it('can handle multiple cases', function () { | ||
$stringable = new class implements Stringable { | ||
public function __toString() | ||
{ | ||
return 'stringable'; | ||
} | ||
}; | ||
|
||
class DataTestBreakableValidation extends Data | ||
{ | ||
// strict types are not a problem since this all happens in the data context which is not strict | ||
public function __construct( | ||
// Takes everything | ||
public mixed $mixed, | ||
// Takes string, bool, float, int, class implementing Stringable | ||
public string $string, | ||
// Takes string, bool, float, int | ||
public bool $bool, | ||
// Takes bool, float, int | ||
public int $int, | ||
// Takes bool, float, int | ||
public float $float, | ||
// Takes array | ||
public array $array, | ||
// Takes string, bool, float, int, class implementing Stringable, null | ||
public ?string $nullable, | ||
) { | ||
} | ||
} | ||
|
||
|
||
$data = new DataTestBreakableValidation( | ||
'mixed', | ||
'string', | ||
'1d', | ||
'12', | ||
3.14, | ||
[], | ||
null, | ||
); | ||
|
||
expect($data)->toBeInstanceOf(DataTestBreakableValidation::class); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.