Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions camel/BaseDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,86 @@
namespace Knuckles\Camel;

use Illuminate\Contracts\Support\Arrayable;
use Spatie\DataTransferObject\DataTransferObject;
use Spatie\LaravelData\Data;


class BaseDTO extends DataTransferObject implements Arrayable, \ArrayAccess
class BaseDTO implements Arrayable, \ArrayAccess
{
/**
* @var array $custom
* Added so end-users can dynamically add additional properties for their own use.
*/
public array $custom = [];

public function __construct(array $parameters = [])
{
// Initialize all properties to their default values first
$this->initializeProperties();

foreach ($parameters as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $this->castProperty($key, $value);
}
}
}

protected function initializeProperties(): void
{
$reflection = new \ReflectionClass($this);

foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$name = $property->getName();

// Skip if already initialized (has a default value)
if ($property->hasDefaultValue()) {
continue;
}

$type = $property->getType();
if ($type && $type->allowsNull()) {
$this->$name = null;
}
}
}

protected function castProperty(string $key, mixed $value): mixed
{
// If the value is already the correct type, return it as-is
if (!is_array($value)) {
return $value;
}

// Get property type through reflection
$reflection = new \ReflectionClass($this);
if (!$reflection->hasProperty($key)) {
return $value;
}

$property = $reflection->getProperty($key);
$type = $property->getType();

if ($type && $type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
$className = $type->getName();

// If it's a DTO class in our namespace, instantiate it
if (class_exists($className) && is_subclass_of($className, self::class)) {
return new $className($value);
}

// If it's another class in our namespace that has a constructor accepting arrays
if (class_exists($className)) {
try {
return new $className($value);
} catch (\Throwable $e) {
// If instantiation fails, return the original value
return $value;
}
}
}

return $value;
}

public static function create(BaseDTO|array $data, BaseDTO|array $inheritFrom = []): static
{
if ($data instanceof static) {
Expand Down Expand Up @@ -49,6 +118,15 @@ protected function parseArray(array $array): array
return $array;
}

public function toArray(): array
{
$array = [];
foreach (get_object_vars($this) as $property => $value) {
$array[$property] = $value;
}
return $this->parseArray($array);
}

public static function make(array|self $data): static
{
return $data instanceof static ? $data : new static($data);
Expand All @@ -73,4 +151,22 @@ public function offsetUnset(mixed $offset): void
{
unset($this->$offset);
}

public function except(string ...$keys): array
{
$array = [];
foreach (get_object_vars($this) as $property => $value) {
if (!in_array($property, $keys)) {
$array[$property] = $value;
}
}
return $this->parseArray($array);
}

public static function arrayOf(array $items): array
{
return array_map(function ($item) {
return $item instanceof static ? $item : new static($item);
}, $items);
}
}
3 changes: 1 addition & 2 deletions camel/BaseDTOCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;
use Spatie\DataTransferObject\DataTransferObjectCollection;

/**
* @template T of \Spatie\DataTransferObject\DataTransferObject
* @template T of \Knuckles\Camel\BaseDTO
*/
class BaseDTOCollection extends Collection
{
Expand Down
8 changes: 5 additions & 3 deletions camel/Extraction/ExtractedEndpointData.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,17 @@ public function endpointId()
*/
public function forSerialisation()
{
$copy = $this->except(
$copyArray = $this->except(
// Get rid of all duplicate data
'cleanQueryParameters', 'cleanUrlParameters', 'fileParameters', 'cleanBodyParameters',
// and objects used only in extraction
'route', 'controller', 'method', 'auth',
);
// Remove these, since they're on the parent group object
$copy->metadata = $copy->metadata->except('groupName', 'groupDescription');
if (isset($copyArray['metadata']) && $copyArray['metadata'] instanceof Metadata) {
$copyArray['metadata'] = $copyArray['metadata']->except('groupName', 'groupDescription');
}

return $copy;
return $copyArray;
}
}
6 changes: 1 addition & 5 deletions camel/Output/Parameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ class Parameter extends \Knuckles\Camel\Extraction\Parameter

public function toArray(): array
{
if (empty($this->exceptKeys)) {
return $this->except('__fields')->toArray();
}

return parent::toArray();
return $this->except('__fields');
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"ramsey/uuid": "^4.2.2",
"shalvah/clara": "^3.1.0",
"shalvah/upgrader": ">=0.6.0",
"spatie/data-transfer-object": "^2.6|^3.0",
"spatie/laravel-data": "^3.0|^4.0",
"symfony/var-exporter": "^6.0|^7.0",
"symfony/yaml": "^6.0|^7.0"
},
Expand Down