Skip to content

Commit

Permalink
Support should return null (#264)
Browse files Browse the repository at this point in the history
* Support should return null

* Typo

* Optimize

* Optimize

* Optimize

* Fix

---------

Co-authored-by: Deeka Wong <[email protected]>
  • Loading branch information
huangdijia and huangdijia authored Jul 5, 2023
1 parent ac5d5b9 commit 738c991
Showing 1 changed file with 87 additions and 29 deletions.
116 changes: 87 additions & 29 deletions src/ValidatedDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,14 @@ abstract class ValidatedDTO

protected bool $requireCasting = false;

protected ?ValidatorInterface $validator = null;

/**
* @throws ValidationException
*/
public function __construct(protected array $data, ?string $scene = null)
public function __construct(protected array $data, protected ?string $scene = null)
{
$rules = $this->rules();

if ($scene) {
if (! $this->hasScene($scene) || ! $keys = $this->getSceneKeys($scene)) {
throw new InvalidArgumentException(sprintf('Scene [%s] is not defined or empty.', $scene));
}

$rules = Arr::only($rules, $keys);
}

$validator = ApplicationContext::getContainer()
->get(ValidatorFactoryInterface::class)
->make(
$data,
$rules,
$this->messages(),
$this->attributes()
);

! $validator->fails() ? $this->passedValidation($validator, $rules) : $this->failedValidation($validator);
$this->isValidData() ? $this->passedValidation() : $this->failedValidation();
}

public function __get(string $name): mixed
Expand All @@ -77,6 +60,14 @@ public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
return json_encode($this->validatedData, $options);
}

/**
* Returns the DTO validated data in a pretty JSON string format.
*/
public function toPrettyJson(): string
{
return json_encode($this->validatedData, JSON_PRETTY_PRINT);
}

/**
* Creates a new model with the DTO validated data.
*/
Expand All @@ -85,12 +76,44 @@ public function toModel(string $model): Model
return new $model($this->validatedData);
}

protected function getRules()
{
$rules = $this->rules();

if ($scene = $this->scene) {
if (! $this->hasScene($scene) || ! $keys = $this->getSceneKeys($scene)) {
throw new InvalidArgumentException(sprintf('Scene [%s] is not defined or empty.', $scene));
}

$rules = Arr::only($rules, $keys);
}

return $rules;
}

/**
* Checks if the data is valid for the DTO.
*/
protected function isValidData(): bool
{
$this->validator = ApplicationContext::getContainer()
->get(ValidatorFactoryInterface::class)
->make(
$this->data,
$this->getRules(),
$this->messages(),
$this->attributes()
);

return ! $this->validator->fails();
}

/**
* Handles a passed validation attempt.
*/
protected function passedValidation(ValidatorInterface $validator, array $rules = []): void
protected function passedValidation(array $rules = []): void
{
$this->validatedData = $validator->validated();
$this->validatedData = $this->validatedData();

foreach ($this->defaults() as $key => $value) {
if (! in_array($key, array_keys($rules))) {
Expand All @@ -111,23 +134,48 @@ protected function passedValidation(ValidatorInterface $validator, array $rules
continue;
}

if (! $casts[$key] instanceof Castable) {
throw new CastTargetException($key);
}
$formatted = $this->shouldReturnNull($key, $value)
? null
: $this->castValue($casts[$key], $key, $value);

$formatted = $casts[$key]->cast($key, $value);
$this->validatedData[$key] = $formatted;
}
}

protected function validatedData(): array
{
return $this->validator->validated();
}

/**
* Handles a failed validation attempt.
*
* @throws ValidationException
*/
protected function failedValidation(ValidatorInterface $validator): void
protected function failedValidation(): void
{
throw new ValidationException($validator);
throw new ValidationException($this->validator);
}

/**
* @throws CastTargetException
*/
protected function castValue(mixed $cast, string $key, mixed $value): mixed
{
if ($cast instanceof Castable) {
return $cast->cast($key, $value);
}

if (! is_callable($cast)) {
throw new CastTargetException($key);
}

return $cast($key, $value);
}

protected function shouldReturnNull(string $key, mixed $value): bool
{
return is_null($value) && $this->isOptionalProperty($key);
}

protected function hasScene(string $scene): bool
Expand Down Expand Up @@ -182,4 +230,14 @@ protected function casts(): array
{
return [];
}

private function isOptionalProperty(string $property): bool
{
$rules = $this->getRules();
$propertyRules = is_array($rules[$property])
? $rules[$property]
: explode('|', $rules[$property]);

return in_array('optional', $propertyRules) || in_array('nullable', $propertyRules);
}
}

0 comments on commit 738c991

Please sign in to comment.