diff --git a/shared/lib/Chrono/Entity/EntityBaseAbstract.php b/shared/lib/Chrono/Entity/EntityBaseAbstract.php index f1ea194..3481ecb 100644 --- a/shared/lib/Chrono/Entity/EntityBaseAbstract.php +++ b/shared/lib/Chrono/Entity/EntityBaseAbstract.php @@ -22,7 +22,7 @@ use KTXF\Resource\Provider\Node\NodeBaseAbstract; abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface { protected string $type = 'chrono:entity'; - protected EntityPropertiesBaseInterface $properties; + protected EventObject|TaskObject|JournalObject $properties; protected function nodeIdentifier(): EntityIdentifier { return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], $this->data[static::PROPERTY_COLLECTION], $this->data[static::PROPERTY_IDENTIFIER]); @@ -31,7 +31,7 @@ abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBase /** * @inheritDoc */ - public function getProperties(): EntityPropertiesBaseInterface { - return $this->properties; + public function getProperties(): EventObject|TaskObject|JournalObject { + return $this->properties ??= new EventObject(); } } diff --git a/shared/lib/Chrono/Entity/EntityBaseInterface.php b/shared/lib/Chrono/Entity/EntityBaseInterface.php index 85af2d6..4d4968e 100644 --- a/shared/lib/Chrono/Entity/EntityBaseInterface.php +++ b/shared/lib/Chrono/Entity/EntityBaseInterface.php @@ -14,10 +14,10 @@ use KTXF\Resource\Provider\Node\NodeBaseInterface; interface EntityBaseInterface extends NodeBaseInterface { /** - * Gets the entity properties - * + * Gets the entity properties: the typed chrono entity object + * * @since 2025.05.01 */ - public function getProperties(): EntityPropertiesBaseInterface|EntityPropertiesMutableInterface; + public function getProperties(): EventObject|TaskObject|JournalObject; } diff --git a/shared/lib/Chrono/Entity/EntityMutableAbstract.php b/shared/lib/Chrono/Entity/EntityMutableAbstract.php index da0ecbc..3d6775e 100644 --- a/shared/lib/Chrono/Entity/EntityMutableAbstract.php +++ b/shared/lib/Chrono/Entity/EntityMutableAbstract.php @@ -23,7 +23,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface; abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface { protected string $type = 'chrono:entity'; - protected EntityPropertiesMutableInterface $properties; + protected EventObject|TaskObject|JournalObject $properties; protected function nodeIdentifier(): EntityIdentifier { return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], (string) $this->data[static::PROPERTY_COLLECTION], (string) $this->data[static::PROPERTY_IDENTIFIER]); @@ -32,16 +32,36 @@ abstract class EntityMutableAbstract extends NodeMutableAbstract implements Enti /** * @inheritDoc */ - public function getProperties(): EntityPropertiesMutableInterface { - return $this->properties; + public function jsonDeserialize(array|string $data): static { + if (is_string($data)) { + $data = json_decode($data, true); + } + + $properties = $data[static::PROPERTY_PROPERTIES] ?? null; + unset($data[static::PROPERTY_PROPERTIES]); + + parent::jsonDeserialize($data); + + if (is_array($properties)) { + $this->properties = EntityType::fromData($properties)->instantiate()->jsonDeserialize($properties); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function getProperties(): EventObject|TaskObject|JournalObject { + return $this->properties ??= new EventObject(); } /** * @inheritDoc */ public function setProperties(NodePropertiesMutableInterface $value): static { - if (!$value instanceof EntityPropertiesMutableInterface) { - throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface'); + if (!$value instanceof EventObject && !$value instanceof TaskObject && !$value instanceof JournalObject) { + throw new \InvalidArgumentException('Properties must be an EventObject, TaskObject or JournalObject'); } $this->properties = $value; diff --git a/shared/lib/Chrono/Entity/EntityMutableInterface.php b/shared/lib/Chrono/Entity/EntityMutableInterface.php index 4ca5bdd..8a0787b 100644 --- a/shared/lib/Chrono/Entity/EntityMutableInterface.php +++ b/shared/lib/Chrono/Entity/EntityMutableInterface.php @@ -17,10 +17,10 @@ use KTXF\Resource\Provider\Node\NodeMutableInterface; interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface { /** - * Gets the entity properties (mutable) - * + * Gets the entity properties: the typed chrono entity object + * * @since 2025.05.01 */ - public function getProperties(): EntityPropertiesMutableInterface; + public function getProperties(): EventObject|TaskObject|JournalObject; } diff --git a/shared/lib/Chrono/Entity/EntityPropertiesBaseAbstract.php b/shared/lib/Chrono/Entity/EntityPropertiesBaseAbstract.php index 20c85ec..15f26b8 100644 --- a/shared/lib/Chrono/Entity/EntityPropertiesBaseAbstract.php +++ b/shared/lib/Chrono/Entity/EntityPropertiesBaseAbstract.php @@ -9,14 +9,28 @@ declare(strict_types=1); namespace KTXF\Chrono\Entity; -use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract; +use KTXF\Json\JsonSerializableObject; -abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface { +/** + * Common base of the typed chrono entity objects ({@see EventObject}, + * {@see TaskObject}, {@see JournalObject}). + * + * Gives providers a single class to build or extend entity properties + * against without involving the entity node wrapper, and one home for the + * shared behavior: the type/schema markers and the JSON serialization / + * deserialization machinery inherited from JsonSerializableObject. + */ +abstract class EntityPropertiesBaseAbstract extends JsonSerializableObject implements EntityPropertiesBaseInterface { + + public string $type = 'event'; + public int $schema = 1; + + public function type(): string { + return $this->type; + } + + public function schema(): int { + return $this->schema; + } - /** - * @inheritDoc - */ - public function getDataRaw(): array|string|null { - return $this->data[self::PROPERTY_DATA] ?? null; - } } diff --git a/shared/lib/Chrono/Entity/EntityPropertiesBaseInterface.php b/shared/lib/Chrono/Entity/EntityPropertiesBaseInterface.php index 0fa6bca..c21432a 100644 --- a/shared/lib/Chrono/Entity/EntityPropertiesBaseInterface.php +++ b/shared/lib/Chrono/Entity/EntityPropertiesBaseInterface.php @@ -14,8 +14,5 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface; interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface { public const JSON_TYPE = 'chrono:entity'; - public const PROPERTY_DATA = 'data'; - - public function getDataRaw(): array|string|null; } diff --git a/shared/lib/Chrono/Entity/EntityPropertiesMutableAbstract.php b/shared/lib/Chrono/Entity/EntityPropertiesMutableAbstract.php index 0a94ce2..0dfaabc 100644 --- a/shared/lib/Chrono/Entity/EntityPropertiesMutableAbstract.php +++ b/shared/lib/Chrono/Entity/EntityPropertiesMutableAbstract.php @@ -9,22 +9,4 @@ declare(strict_types=1); namespace KTXF\Chrono\Entity; -abstract class EntityPropertiesMutableAbstract extends EntityPropertiesBaseAbstract implements EntityPropertiesMutableInterface { - - public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE; - - public function jsonDeserialize(array|string $data): static { - if (is_string($data)) { - $data = json_decode($data, true); - } - - $this->data = $data; - - return $this; - } - - public function setDataRaw(array|string|null $value): static { - $this->data[self::PROPERTY_DATA] = $value; - return $this; - } -} +abstract class EntityPropertiesMutableAbstract extends EntityPropertiesBaseAbstract implements EntityPropertiesMutableInterface {} diff --git a/shared/lib/Chrono/Entity/EntityPropertiesMutableInterface.php b/shared/lib/Chrono/Entity/EntityPropertiesMutableInterface.php index 71caee9..b87b0b8 100644 --- a/shared/lib/Chrono/Entity/EntityPropertiesMutableInterface.php +++ b/shared/lib/Chrono/Entity/EntityPropertiesMutableInterface.php @@ -11,8 +11,4 @@ namespace KTXF\Chrono\Entity; use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface; -interface EntityPropertiesMutableInterface extends EntityPropertiesBaseInterface, NodePropertiesMutableInterface { - - public function setDataRaw(array|string|null $value): static; - -} +interface EntityPropertiesMutableInterface extends EntityPropertiesBaseInterface, NodePropertiesMutableInterface {} diff --git a/shared/lib/Chrono/Entity/EntityType.php b/shared/lib/Chrono/Entity/EntityType.php new file mode 100644 index 0000000..872246b --- /dev/null +++ b/shared/lib/Chrono/Entity/EntityType.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace KTXF\Chrono\Entity; + +enum EntityType: string { + case Event = 'event'; + case Task = 'task'; + case Journal = 'journal'; + + /** + * Canonical derivation of the entity type from a serialized payload's + * own type marker. + */ + public static function fromData(array $data): self { + return self::tryFrom((string)($data['type'] ?? '')) ?? self::Event; + } + + /** Constructs the typed entity object for this type. */ + public function instantiate(): EventObject|TaskObject|JournalObject { + return match ($this) { + self::Event => new EventObject(), + self::Task => new TaskObject(), + self::Journal => new JournalObject(), + }; + } +} diff --git a/shared/lib/Chrono/Entity/EventObject.php b/shared/lib/Chrono/Entity/EventObject.php index 981952c..0875bc9 100644 --- a/shared/lib/Chrono/Entity/EventObject.php +++ b/shared/lib/Chrono/Entity/EventObject.php @@ -17,8 +17,6 @@ use KTXF\Chrono\Entity\Property\EventOccurrenceObject; class EventObject extends EventCommonObject { // Meta Information - public string $type = 'event'; - public int $version = 1; public string|null $urid = null; public ?DateTimeInterface $created = null; public ?DateTimeInterface $modified = null; diff --git a/shared/lib/Chrono/Entity/JournalObject.php b/shared/lib/Chrono/Entity/JournalObject.php index 74c648d..7f3f695 100644 --- a/shared/lib/Chrono/Entity/JournalObject.php +++ b/shared/lib/Chrono/Entity/JournalObject.php @@ -9,11 +9,9 @@ use KTXF\Chrono\Entity\Property\AttachmentCollection; use KTXF\Chrono\Entity\Property\JournalStatusTypes; use KTXF\Chrono\Entity\Property\JournalVisibilityTypes; use KTXF\Chrono\Entity\Property\TagCollection; -use KTXF\Json\JsonSerializableObject; -class JournalObject extends JsonSerializableObject { +class JournalObject extends EntityPropertiesMutableAbstract { public string $type = 'journal'; - public int $version = 1; public ?string $urid = null; public ?DateTimeInterface $created = null; public ?DateTimeInterface $modified = null; diff --git a/shared/lib/Chrono/Entity/Property/EventCommonObject.php b/shared/lib/Chrono/Entity/Property/EventCommonObject.php index 6b16752..4973acc 100644 --- a/shared/lib/Chrono/Entity/Property/EventCommonObject.php +++ b/shared/lib/Chrono/Entity/Property/EventCommonObject.php @@ -13,9 +13,9 @@ use DateInterval; use DateTime; use DateTimeImmutable; use DateTimeZone; -use KTXF\Json\JsonSerializableObject; +use KTXF\Chrono\Entity\EntityPropertiesMutableAbstract; -class EventCommonObject extends JsonSerializableObject { +class EventCommonObject extends EntityPropertiesMutableAbstract { public int|null $sequence = null; public DateTimeZone|null $timeZone = null; diff --git a/shared/lib/Chrono/Entity/TaskObject.php b/shared/lib/Chrono/Entity/TaskObject.php index 6a79fa7..1b8dc7d 100644 --- a/shared/lib/Chrono/Entity/TaskObject.php +++ b/shared/lib/Chrono/Entity/TaskObject.php @@ -10,11 +10,9 @@ use KTXF\Chrono\Entity\Property\TagCollection; use KTXF\Chrono\Entity\Property\TaskRecurrenceObject; use KTXF\Chrono\Entity\Property\TaskStatusTypes; use KTXF\Chrono\Entity\Property\TaskSubtaskCollection; -use KTXF\Json\JsonSerializableObject; -class TaskObject extends JsonSerializableObject { +class TaskObject extends EntityPropertiesMutableAbstract { public string $type = 'task'; - public int $version = 1; public ?string $urid = null; public ?DateTimeInterface $created = null; public ?DateTimeInterface $modified = null; diff --git a/shared/lib/Chrono/Service/ServiceEntityMutableInterface.php b/shared/lib/Chrono/Service/ServiceEntityMutableInterface.php index e2cb935..fcccdf2 100644 --- a/shared/lib/Chrono/Service/ServiceEntityMutableInterface.php +++ b/shared/lib/Chrono/Service/ServiceEntityMutableInterface.php @@ -12,6 +12,7 @@ namespace KTXF\Chrono\Service; use KTXF\Chrono\Entity\EntityBaseInterface; use KTXF\Chrono\Entity\EntityMutableInterface; use KTXF\Chrono\Entity\EntityPropertiesMutableInterface; +use KTXF\Chrono\Entity\EntityType; use KTXF\Resource\Identifier\CollectionIdentifierInterface; use KTXF\Resource\Identifier\EntityIdentifierInterface; @@ -31,13 +32,14 @@ interface ServiceEntityMutableInterface { public const CAPABILITY_ENTITY_MOVE = 'EntityMove'; /** - * Creates a fresh entity instance for composition + * Creates a fresh entity instance for composition, with its properties + * object constructed for the given entity type * * @since 2025.05.01 * * @return EntityMutableInterface Fresh entity object */ - public function entityFresh(): EntityMutableInterface; + public function entityFresh(EntityType $type = EntityType::Event): EntityMutableInterface; /** * Creates/imports an entity into a collection