dep: DTO
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
class Arr
|
||||
{
|
||||
public static function only($array, $keys): array
|
||||
{
|
||||
return array_intersect_key($array, array_flip((array) $keys));
|
||||
}
|
||||
|
||||
public static function except($array, $keys): array
|
||||
{
|
||||
return static::forget($array, $keys);
|
||||
}
|
||||
|
||||
public static function forget($array, $keys): array
|
||||
{
|
||||
$keys = (array) $keys;
|
||||
|
||||
if (count($keys) === 0) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
// If the exact key exists in the top-level, remove it
|
||||
if (static::exists($array, $key)) {
|
||||
unset($array[$key]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the key is using dot-notation
|
||||
if (! str_contains($key, '.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are dealing with dot-notation, recursively handle i
|
||||
$parts = explode('.', $key);
|
||||
$key = array_shift($parts);
|
||||
|
||||
if (static::exists($array, $key) && static::accessible($array[$key])) {
|
||||
$array[$key] = static::forget($array[$key], implode('.', $parts));
|
||||
|
||||
if (count($array[$key]) === 0) {
|
||||
unset($array[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function get($array, $key, $default = null)
|
||||
{
|
||||
if (! static::accessible($array)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (is_null($key)) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
if (static::exists($array, $key)) {
|
||||
return $array[$key];
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false) {
|
||||
return $array[$key] ?? $default;
|
||||
}
|
||||
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (static::accessible($array) && static::exists($array, $segment)) {
|
||||
$array = $array[$segment];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function accessible($value)
|
||||
{
|
||||
return is_array($value) || $value instanceof ArrayAccess;
|
||||
}
|
||||
|
||||
public static function exists($array, $key): bool
|
||||
{
|
||||
if ($array instanceof ArrayAccess) {
|
||||
return $array->offsetExists($key);
|
||||
}
|
||||
|
||||
return array_key_exists($key, $array);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Attributes;
|
||||
|
||||
use Attribute;
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
use Spatie\DataTransferObject\Exceptions\InvalidCasterClass;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
|
||||
class CastWith
|
||||
{
|
||||
public array $args;
|
||||
|
||||
public function __construct(
|
||||
public string $casterClass,
|
||||
mixed ...$args
|
||||
) {
|
||||
if (! is_subclass_of($this->casterClass, Caster::class)) {
|
||||
throw new InvalidCasterClass($this->casterClass);
|
||||
}
|
||||
|
||||
$this->args = $args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Attributes;
|
||||
|
||||
use Attribute;
|
||||
use JetBrains\PhpStorm\Immutable;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use ReflectionUnionType;
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
class DefaultCast
|
||||
{
|
||||
public function __construct(
|
||||
#[Immutable]
|
||||
private string $targetClass,
|
||||
#[Immutable]
|
||||
private string $casterClass,
|
||||
) {
|
||||
}
|
||||
|
||||
public function accepts(ReflectionProperty $property): bool
|
||||
{
|
||||
$type = $property->getType();
|
||||
|
||||
/** @var \ReflectionNamedType[]|null $types */
|
||||
$types = match ($type::class) {
|
||||
ReflectionNamedType::class => [$type],
|
||||
ReflectionUnionType::class => $type->getTypes(),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (! $types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($types as $type) {
|
||||
if ($type->getName() !== $this->targetClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function resolveCaster(): Caster
|
||||
{
|
||||
return new $this->casterClass();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Attributes;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
|
||||
class MapFrom
|
||||
{
|
||||
public function __construct(
|
||||
public string | int $name,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Attributes;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
|
||||
class MapTo
|
||||
{
|
||||
public function __construct(
|
||||
public string $name,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Attributes;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
class Strict
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject;
|
||||
|
||||
interface Caster
|
||||
{
|
||||
public function cast(mixed $value): mixed;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Casters;
|
||||
|
||||
use ArrayAccess;
|
||||
use LogicException;
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
use Traversable;
|
||||
|
||||
class ArrayCaster implements Caster
|
||||
{
|
||||
public function __construct(
|
||||
private array $types,
|
||||
private string $itemType,
|
||||
) {
|
||||
}
|
||||
|
||||
public function cast(mixed $value): array | ArrayAccess
|
||||
{
|
||||
foreach ($this->types as $type) {
|
||||
if ($type == 'array') {
|
||||
return $this->mapInto(
|
||||
destination: [],
|
||||
items: $value
|
||||
);
|
||||
}
|
||||
|
||||
if (is_subclass_of($type, ArrayAccess::class)) {
|
||||
return $this->mapInto(
|
||||
destination: new $type(),
|
||||
items: $value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new LogicException(
|
||||
"Caster [ArrayCaster] may only be used to cast arrays or objects that implement ArrayAccess."
|
||||
);
|
||||
}
|
||||
|
||||
private function mapInto(array | ArrayAccess $destination, mixed $items): array | ArrayAccess
|
||||
{
|
||||
if ($destination instanceof ArrayAccess && ! is_subclass_of($destination, Traversable::class)) {
|
||||
throw new LogicException(
|
||||
"Caster [ArrayCaster] may only be used to cast ArrayAccess objects that are traversable."
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
$destination[$key] = $this->castItem($item);
|
||||
}
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
private function castItem(mixed $data)
|
||||
{
|
||||
if ($data instanceof $this->itemType) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
return new $this->itemType(...$data);
|
||||
}
|
||||
|
||||
throw new LogicException(
|
||||
"Caster [ArrayCaster] each item must be an array or an instance of the specified item type [{$this->itemType}]."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Casters;
|
||||
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
|
||||
class DataTransferObjectCaster implements Caster
|
||||
{
|
||||
public function __construct(
|
||||
private array $classNames
|
||||
) {
|
||||
}
|
||||
|
||||
public function cast(mixed $value): DataTransferObject
|
||||
{
|
||||
foreach ($this->classNames as $className) {
|
||||
if ($value instanceof $className) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return new $this->classNames[0](...$value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
use Spatie\DataTransferObject\Attributes\CastWith;
|
||||
use Spatie\DataTransferObject\Attributes\MapTo;
|
||||
use Spatie\DataTransferObject\Casters\DataTransferObjectCaster;
|
||||
use Spatie\DataTransferObject\Exceptions\UnknownProperties;
|
||||
use Spatie\DataTransferObject\Reflection\DataTransferObjectClass;
|
||||
|
||||
#[CastWith(DataTransferObjectCaster::class)]
|
||||
abstract class DataTransferObject
|
||||
{
|
||||
protected array $exceptKeys = [];
|
||||
|
||||
protected array $onlyKeys = [];
|
||||
|
||||
public function __construct(...$args)
|
||||
{
|
||||
if (is_array($args[0] ?? null)) {
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
$class = new DataTransferObjectClass($this);
|
||||
|
||||
foreach ($class->getProperties() as $property) {
|
||||
$property->setValue(Arr::get($args, $property->name) ?? $this->{$property->name} ?? null);
|
||||
|
||||
$args = Arr::forget($args, $property->name);
|
||||
}
|
||||
|
||||
if ($class->isStrict() && count($args)) {
|
||||
throw UnknownProperties::new(static::class, array_keys($args));
|
||||
}
|
||||
|
||||
$class->validate();
|
||||
}
|
||||
|
||||
public static function arrayOf(array $arrayOfParameters): array
|
||||
{
|
||||
return array_map(
|
||||
fn (mixed $parameters) => new static($parameters),
|
||||
$arrayOfParameters
|
||||
);
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$class = new ReflectionClass(static::class);
|
||||
|
||||
$properties = $class->getProperties(ReflectionProperty::IS_PUBLIC);
|
||||
|
||||
foreach ($properties as $property) {
|
||||
if ($property->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapToAttribute = $property->getAttributes(MapTo::class);
|
||||
$name = count($mapToAttribute) ? $mapToAttribute[0]->newInstance()->name : $property->getName();
|
||||
|
||||
$data[$name] = $property->getValue($this);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function only(string ...$keys): static
|
||||
{
|
||||
$dataTransferObject = clone $this;
|
||||
|
||||
$dataTransferObject->onlyKeys = [...$this->onlyKeys, ...$keys];
|
||||
|
||||
return $dataTransferObject;
|
||||
}
|
||||
|
||||
public function except(string ...$keys): static
|
||||
{
|
||||
$dataTransferObject = clone $this;
|
||||
|
||||
$dataTransferObject->exceptKeys = [...$this->exceptKeys, ...$keys];
|
||||
|
||||
return $dataTransferObject;
|
||||
}
|
||||
|
||||
public function clone(...$args): static
|
||||
{
|
||||
return new static(...array_merge($this->toArray(), $args));
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
if (count($this->onlyKeys)) {
|
||||
$array = Arr::only($this->all(), $this->onlyKeys);
|
||||
} else {
|
||||
$array = Arr::except($this->all(), $this->exceptKeys);
|
||||
}
|
||||
|
||||
$array = $this->parseArray($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
protected function parseArray(array $array): array
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if ($value instanceof DataTransferObject) {
|
||||
$array[$key] = $value->toArray();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_array($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$array[$key] = $this->parseArray($value);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
|
||||
class InvalidCasterClass extends Exception
|
||||
{
|
||||
public function __construct(string $className)
|
||||
{
|
||||
$expected = Caster::class;
|
||||
|
||||
parent::__construct(
|
||||
"Class `{$className}` doesn't implement {$expected} and can't be used as a caster"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnknownProperties extends Exception
|
||||
{
|
||||
public static function new(string $dtoClass, array $fields): self
|
||||
{
|
||||
$properties = json_encode($fields);
|
||||
|
||||
return new self("Unknown properties provided to `{$dtoClass}`: {$properties}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
|
||||
class ValidationException extends Exception
|
||||
{
|
||||
public function __construct(
|
||||
public DataTransferObject $dataTransferObject,
|
||||
public array $validationErrors,
|
||||
) {
|
||||
$className = $dataTransferObject::class;
|
||||
|
||||
$messages = [];
|
||||
|
||||
foreach ($validationErrors as $fieldName => $errorsForField) {
|
||||
/** @var \Spatie\DataTransferObject\Validation\ValidationResult $errorForField */
|
||||
foreach ($errorsForField as $errorForField) {
|
||||
$messages[] = "\t - `{$className}->{$fieldName}`: {$errorForField->message}";
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct("Validation errors:" . PHP_EOL . implode(PHP_EOL, $messages));
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Reflection;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
use Spatie\DataTransferObject\Attributes\Strict;
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Spatie\DataTransferObject\Exceptions\ValidationException;
|
||||
|
||||
class DataTransferObjectClass
|
||||
{
|
||||
private ReflectionClass $reflectionClass;
|
||||
|
||||
private DataTransferObject $dataTransferObject;
|
||||
|
||||
private bool $isStrict;
|
||||
|
||||
public function __construct(DataTransferObject $dataTransferObject)
|
||||
{
|
||||
$this->reflectionClass = new ReflectionClass($dataTransferObject);
|
||||
$this->dataTransferObject = $dataTransferObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Spatie\DataTransferObject\Reflection\DataTransferObjectProperty[]
|
||||
*/
|
||||
public function getProperties(): array
|
||||
{
|
||||
$publicProperties = array_filter(
|
||||
$this->reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC),
|
||||
fn (ReflectionProperty $property) => ! $property->isStatic()
|
||||
);
|
||||
|
||||
return array_map(
|
||||
fn (ReflectionProperty $property) => new DataTransferObjectProperty(
|
||||
$this->dataTransferObject,
|
||||
$property
|
||||
),
|
||||
$publicProperties
|
||||
);
|
||||
}
|
||||
|
||||
public function validate(): void
|
||||
{
|
||||
$validationErrors = [];
|
||||
|
||||
foreach ($this->getProperties() as $property) {
|
||||
$validators = $property->getValidators();
|
||||
|
||||
foreach ($validators as $validator) {
|
||||
$result = $validator->validate($property->getValue());
|
||||
|
||||
if ($result->isValid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$validationErrors[$property->name][] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($validationErrors)) {
|
||||
throw new ValidationException($this->dataTransferObject, $validationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public function isStrict(): bool
|
||||
{
|
||||
if (! isset($this->isStrict)) {
|
||||
$attribute = null;
|
||||
|
||||
$reflectionClass = $this->reflectionClass;
|
||||
while ($attribute === null && $reflectionClass !== false) {
|
||||
$attribute = $reflectionClass->getAttributes(Strict::class)[0] ?? null;
|
||||
|
||||
$reflectionClass = $reflectionClass->getParentClass();
|
||||
}
|
||||
|
||||
$this->isStrict = $attribute !== null;
|
||||
}
|
||||
|
||||
return $this->isStrict;
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Reflection;
|
||||
|
||||
use JetBrains\PhpStorm\Immutable;
|
||||
use ReflectionAttribute;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use ReflectionType;
|
||||
use ReflectionUnionType;
|
||||
use Spatie\DataTransferObject\Attributes\CastWith;
|
||||
use Spatie\DataTransferObject\Attributes\DefaultCast;
|
||||
use Spatie\DataTransferObject\Attributes\MapFrom;
|
||||
use Spatie\DataTransferObject\Caster;
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Spatie\DataTransferObject\Validator;
|
||||
|
||||
class DataTransferObjectProperty
|
||||
{
|
||||
#[Immutable]
|
||||
public string $name;
|
||||
|
||||
private DataTransferObject $dataTransferObject;
|
||||
|
||||
private ReflectionProperty $reflectionProperty;
|
||||
|
||||
private ?Caster $caster;
|
||||
|
||||
public function __construct(
|
||||
DataTransferObject $dataTransferObject,
|
||||
ReflectionProperty $reflectionProperty
|
||||
) {
|
||||
$this->dataTransferObject = $dataTransferObject;
|
||||
$this->reflectionProperty = $reflectionProperty;
|
||||
|
||||
$this->name = $this->resolveMappedProperty();
|
||||
|
||||
$this->caster = $this->resolveCaster();
|
||||
}
|
||||
|
||||
public function setValue(mixed $value): void
|
||||
{
|
||||
if ($this->caster && $value !== null) {
|
||||
$value = $this->caster->cast($value);
|
||||
}
|
||||
|
||||
$this->reflectionProperty->setValue($this->dataTransferObject, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Spatie\DataTransferObject\Validator[]
|
||||
*/
|
||||
public function getValidators(): array
|
||||
{
|
||||
$attributes = $this->reflectionProperty->getAttributes(
|
||||
Validator::class,
|
||||
ReflectionAttribute::IS_INSTANCEOF
|
||||
);
|
||||
|
||||
return array_map(
|
||||
fn (ReflectionAttribute $attribute) => $attribute->newInstance(),
|
||||
$attributes
|
||||
);
|
||||
}
|
||||
|
||||
public function getValue(): mixed
|
||||
{
|
||||
return $this->reflectionProperty->getValue($this->dataTransferObject);
|
||||
}
|
||||
|
||||
private function resolveCaster(): ?Caster
|
||||
{
|
||||
$attributes = $this->reflectionProperty->getAttributes(CastWith::class);
|
||||
|
||||
if (! count($attributes)) {
|
||||
$attributes = $this->resolveCasterFromType();
|
||||
}
|
||||
|
||||
if (! count($attributes)) {
|
||||
return $this->resolveCasterFromDefaults();
|
||||
}
|
||||
|
||||
/** @var \Spatie\DataTransferObject\Attributes\CastWith $attribute */
|
||||
$attribute = $attributes[0]->newInstance();
|
||||
|
||||
return new $attribute->casterClass(
|
||||
array_map(fn ($type) => $this->resolveTypeName($type), $this->extractTypes()),
|
||||
...$attribute->args
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveCasterFromType(): array
|
||||
{
|
||||
foreach ($this->extractTypes() as $type) {
|
||||
$name = $this->resolveTypeName($type);
|
||||
|
||||
if (! class_exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reflectionClass = new ReflectionClass($name);
|
||||
|
||||
do {
|
||||
$attributes = $reflectionClass->getAttributes(CastWith::class);
|
||||
|
||||
$reflectionClass = $reflectionClass->getParentClass();
|
||||
} while (! count($attributes) && $reflectionClass);
|
||||
|
||||
if (count($attributes) > 0) {
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function resolveCasterFromDefaults(): ?Caster
|
||||
{
|
||||
$defaultCastAttributes = [];
|
||||
|
||||
$class = $this->reflectionProperty->getDeclaringClass();
|
||||
|
||||
do {
|
||||
array_push($defaultCastAttributes, ...$class->getAttributes(DefaultCast::class));
|
||||
|
||||
$class = $class->getParentClass();
|
||||
} while ($class !== false);
|
||||
|
||||
if (! count($defaultCastAttributes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($defaultCastAttributes as $defaultCastAttribute) {
|
||||
/** @var \Spatie\DataTransferObject\Attributes\DefaultCast $defaultCast */
|
||||
$defaultCast = $defaultCastAttribute->newInstance();
|
||||
|
||||
if ($defaultCast->accepts($this->reflectionProperty)) {
|
||||
return $defaultCast->resolveCaster();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolveMappedProperty(): string | int
|
||||
{
|
||||
$attributes = $this->reflectionProperty->getAttributes(MapFrom::class);
|
||||
|
||||
if (! count($attributes)) {
|
||||
return $this->reflectionProperty->name;
|
||||
}
|
||||
|
||||
return $attributes[0]->newInstance()->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionNamedType[]
|
||||
*/
|
||||
private function extractTypes(): array
|
||||
{
|
||||
$type = $this->reflectionProperty->getType();
|
||||
|
||||
if (! $type) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return match ($type::class) {
|
||||
ReflectionNamedType::class => [$type],
|
||||
ReflectionUnionType::class => $type->getTypes(),
|
||||
};
|
||||
}
|
||||
|
||||
private function resolveTypeName(ReflectionType $type): string
|
||||
{
|
||||
return match ($type->getName()) {
|
||||
'self' => $this->dataTransferObject::class,
|
||||
'parent' => get_parent_class($this->dataTransferObject),
|
||||
default => $type->getName(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject\Validation;
|
||||
|
||||
use JetBrains\PhpStorm\Immutable;
|
||||
|
||||
class ValidationResult
|
||||
{
|
||||
public function __construct(
|
||||
#[Immutable]
|
||||
public bool $isValid,
|
||||
#[Immutable]
|
||||
public ?string $message = null
|
||||
) {
|
||||
}
|
||||
|
||||
public static function valid(): self
|
||||
{
|
||||
return new self(
|
||||
isValid: true,
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalid(string $message): self
|
||||
{
|
||||
return new self(
|
||||
isValid: false,
|
||||
message: $message,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\DataTransferObject;
|
||||
|
||||
use Spatie\DataTransferObject\Validation\ValidationResult;
|
||||
|
||||
interface Validator
|
||||
{
|
||||
public function validate(mixed $value): ValidationResult;
|
||||
}
|
||||
Reference in New Issue
Block a user