Skip to content

Commit

Permalink
Refactor DTO to attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
MGatner committed Aug 24, 2022
1 parent d5bedea commit 7fd2c63
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 98 deletions.
68 changes: 68 additions & 0 deletions src/Objects/AttributesTrait.php
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Tatter\Repositories\Objects;

use OutOfBoundsException;

/**
* Attributes Trait
*
* Adds a protected attributes property with
* read-only access via magic methods.
*/
trait AttributesTrait
{
/**
* @var array<string, mixed>
*/
protected $attributes;

/**
* Sets attributes from an array in one swoop.
*/
public static function fromArray(array $array): static
{
return new static($array);
}

final protected function __construct($attributes)
{
$this->attributes = $attributes;
}

/**
* Gets an attribute.
*
* @throws OutOfBoundsException
*
* @return mixed
*/
final public function __get(string $key)
{
if (array_key_exists($key, $this->attributes)) {
return $this->attributes[$key];
}

throw new OutOfBoundsException('Undefined property: ' . self::class . "::{$key}");
}

/**
* Checks existence of an attribute.
*/
final public function __isset(string $key): bool
{
return isset($this->attributes[$key]);
}

/**
* Gets the attributes.
*
* @return array<string, mixed>
*/
public function toArray(): array
{
return $this->attributes;
}
}
18 changes: 12 additions & 6 deletions src/Objects/DTO.php
Expand Up @@ -9,18 +9,24 @@
*
* Used to pass data between layers.
*
* @AllowDynamicProperties needed for 8.2 but conflicting with Rector
* @immutable
*/
final class DTO extends ValueObject
{
use AttributesTrait;

/**
* @param array<string, scalar|null> $array
* @param self $one
* @param self $two
*/
public function __construct(array $array)
public static function equals(ValueObject $one, ValueObject $two): bool
{
foreach ($array as $key => $value) {
$this->{$key} = $value;
}
$array1 = $one->toArray();
$array2 = $two->toArray();

array_multisort($array1);
array_multisort($array2);

return serialize($array1) === serialize($array2);
}
}
63 changes: 5 additions & 58 deletions src/Objects/Entity.php
Expand Up @@ -4,48 +4,29 @@

namespace Tatter\Repositories\Objects;

use OutOfBoundsException;

/**
* Abstract Entity Class
*
* The home of all business logic for an identity object.
* Override fromArray() and toArray() with any special handling.
* Be sure to leverage Objects\ValueObject for properties that
* Be sure to leverage ValueObjects for properties that
* are composite or complex, or that require validation.
*/
abstract class Entity
{
public const IDENTIFIER = 'id';
use AttributesTrait;

/**
* Sets values array properties.
* Should handle any casting and value object conversions.
*
* @param array<string, scalar|null> $array
*
* @return static
*/
public static function fromArray(array $array): self
{
return new static($array);
}
public const IDENTIFIER = 'id';

/**
* Converts from a data transfer object.
* Can be coming from persistence or user input.
*
* @return static
*/
final public static function fromDTO(DTO $dto): self
final public static function fromDTO(DTO $dto): static
{
return static::fromArray($dto->toArray());
}

final protected function __construct(protected array $attributes)
{
}

/**
* Returns the identity value if it exists, or null.
*
Expand All @@ -56,22 +37,12 @@ final public function getId()
return $this->{static::IDENTIFIER};
}

/**
* Gets the values array for persistence.
*
* @return array<string, mixed>
*/
public function toArray(): array
{
return $this->attributes;
}

/**
* Casts the entity into a data transfer object (e.g. for persistence).
*/
final public function toDTO(): DTO
{
return new DTO($this->toArray());
return DTO::fromArray($this->toArray());
}

/**
Expand All @@ -84,30 +55,6 @@ final public function __set(string $key, $value): void
$this->attributes[$key] = $value;
}

/**
* Gets an attribute.
*
* @throws OutOfBoundsException
*
* @return mixed
*/
final public function __get(string $key)
{
if (array_key_exists($key, $this->attributes)) {
return $this->attributes[$key];
}

throw new OutOfBoundsException('Undefined property: ' . self::class . "::{$key}");
}

/**
* Checks existence of an attribute.
*/
final public function __isset(string $key): bool
{
return isset($this->attributes[$key]);
}

/**
* Unsets an attribute property.
*/
Expand Down
36 changes: 2 additions & 34 deletions src/Objects/ValueObject.php
Expand Up @@ -4,8 +4,6 @@

namespace Tatter\Repositories\Objects;

use InvalidArgumentException;

/**
* Value Object Class
*
Expand All @@ -17,41 +15,11 @@
* - have a private constructor to set values and validate
* - have one or more static named construction methods
* - have only private properties with getters
* - define a proper validate() method
* - define any necessary validation method
*
* @immutable
*/
abstract class ValueObject
{
/**
* $var static $one
* $var static $two
*/
public static function equals(ValueObject $one, ValueObject $two): bool
{
$array1 = $one->toArray();
$array2 = $two->toArray();

array_multisort($array1);
array_multisort($array2);

return serialize($array1) === serialize($array2);
}

/**
* @throws InvalidArgumentException
*/
protected static function validate(): void
{
}

/**
* Casts this into a values array (e.g. for persistence).
*
* @return array<string, mixed>
*/
public function toArray(): array
{
return (array) $this;
}
abstract public static function equals(ValueObject $one, ValueObject $two): bool;
}

0 comments on commit 7fd2c63

Please sign in to comment.