diff --git a/src/Adapter/Filesystem/Apply.php b/src/Adapter/Filesystem/Apply.php deleted file mode 100644 index 5b93b03..0000000 --- a/src/Adapter/Filesystem/Apply.php +++ /dev/null @@ -1,131 +0,0 @@ -diff = $diff; - } - - public function __invoke(Aggregate $source): Aggregate - { - return Aggregate::of( - $this->diff->id(), - $this->applyProperties($source->properties(), $this->diff->properties()), - $this->applyEntities($source->entities(), $this->diff->entities()), - $this->applyOptionals($source->optionals(), $this->diff->optionals()), - $this->applyCollections($source->collections(), $this->diff->collections()), - ); - } - - /** - * @internal - * @psalm-pure - */ - public static function of(Diff $diff): self - { - return new self($diff); - } - - /** - * @param Set $then - * @param Set $now - * - * @return Set - */ - private function applyProperties(Set $then, Set $now): Set - { - return $then->map( - static fn($property) => $now - ->find($property->referenceSame(...)) - ->match( - static fn($diff) => $diff, - static fn() => $property, - ), - ); - } - - /** - * @param Set $then - * @param Set $now - * - * @return Set - */ - private function applyEntities(Set $then, Set $now): Set - { - return $then->map( - fn($entity) => $now - ->find($entity->referenceSame(...)) - ->match( - fn($diff) => Aggregate\Entity::of( - $diff->name(), - $this->applyProperties($entity->properties(), $diff->properties()), - ), - static fn() => $entity, - ), - ); - } - - /** - * @param Set $then - * @param Set $now - * - * @return Set - */ - private function applyOptionals(Set $then, Set $now): Set - { - return $then->map( - fn($optional) => $now - ->find($optional->referenceSame(...)) - ->match( - fn($diff) => Aggregate\Optional::of( - $diff->name(), - $diff - ->properties() - ->map( - fn($properties) => $optional - ->properties() - ->match( - fn($then) => $this->applyProperties($then, $properties), - static fn() => $properties, - ), - ), - ), - static fn() => $optional, - ), - ); - } - - /** - * @param Set $then - * @param Set $now - * - * @return Set - */ - private function applyCollections(Set $then, Set $now): Set - { - return $then->map( - static fn($collection) => $now - ->find($collection->referenceSame(...)) - ->match( - static fn($collection) => $collection, - static fn() => $collection, - ), - ); - } -} diff --git a/src/Adapter/Filesystem/Decode.php b/src/Adapter/Filesystem/Decode.php index dd268a7..d8306d7 100644 --- a/src/Adapter/Filesystem/Decode.php +++ b/src/Adapter/Filesystem/Decode.php @@ -7,11 +7,16 @@ Definition\Aggregate as Definition, Raw\Aggregate, }; -use Innmind\Filesystem\File; +use Innmind\Filesystem\{ + File, + Directory, + Name, +}; use Innmind\Json\Json; use Innmind\Immutable\{ Maybe, Set, + Predicate\Instance, }; /** @@ -32,58 +37,116 @@ private function __construct(Definition $definition) } /** - * @return callable(File): Maybe + * @return callable(Directory): Maybe */ public function __invoke(Aggregate\Id $id = null): callable { /** @psalm-suppress ArgumentTypeCoercion */ $id = match ($id) { - null => fn(File $file) => Aggregate\Id::of( + null => fn(Directory $directory) => Aggregate\Id::of( $this->definition->id()->property(), - $file->name()->toString(), + $directory->name()->toString(), ), - default => static fn(File $file) => $id, + default => static fn(Directory $directory) => $id, }; /** * @psalm-suppress MixedArgument * @psalm-suppress MixedArrayAccess + * @psalm-suppress MixedArgumentTypeCoercion */ - return static fn(File $file) => Maybe::just($file->content()->toString()) - ->map(Json::decode(...)) - ->filter(\is_array(...)) - ->map(static fn($raw) => Aggregate::of( - $id($file), - Set::of(...$raw['properties'])->map(static fn($property) => Aggregate\Property::of( - $property[0], - $property[1], - )), - Set::of(...$raw['entities'])->map(static fn($entity) => Aggregate\Entity::of( - $entity[0], - Set::of(...$entity[1])->map(static fn($property) => Aggregate\Property::of( - $property[0], - $property[1], - )), - )), - Set::of(...$raw['optionals'])->map(static fn($optional) => Aggregate\Optional::of( - $optional[0], - Maybe::of($optional[1])->map(static fn($properties) => Set::of(...$properties)->map( - static fn($property) => Aggregate\Property::of( - $property[0], - $property[1], - ), - )), - )), - Set::of(...$raw['collections'])->map(static fn($collection) => Aggregate\Collection::of( - $collection[0], - Set::of(...$collection[1])->map(static fn($properties) => Set::of(...$properties)->map( - static fn($property) => Aggregate\Property::of( - $property[0], - $property[1], - ), - )), - )), - )); + return static fn(Directory $directory) => Maybe::all( + $directory + ->get(Name::of('properties')) + ->keep(Instance::of(Directory::class)) + ->map( + static fn($properties) => $properties + ->all() + ->keep(Instance::of(File::class)) + ->map(static fn($file) => Aggregate\Property::of( + $file->name()->toString(), + Json::decode($file->content()->toString()), + )) + ->toSet(), + ), + $directory + ->get(Name::of('entities')) + ->keep(Instance::of(Directory::class)) + ->map( + static fn($entities) => $entities + ->all() + ->keep(Instance::of(Directory::class)) + ->map( + static fn($entity) => Aggregate\Entity::of( + $entity->name()->toString(), + $entity + ->all() + ->keep(Instance::of(File::class)) + ->map(static fn($property) => Aggregate\Property::of( + $property->name()->toString(), + Json::decode($property->content()->toString()), + )) + ->toSet(), + ), + ) + ->toSet(), + ), + $directory + ->get(Name::of('optionals')) + ->keep(Instance::of(Directory::class)) + ->map( + static fn($optionals) => $optionals + ->all() + ->keep(Instance::of(Directory::class)) + ->map( + static fn($optional) => Aggregate\Optional::of( + $optional->name()->toString(), + $optional + ->get(Name::of('just')) + ->keep(Instance::of(Directory::class)) + ->map( + static fn($just) => $just + ->all() + ->keep(Instance::of(File::class)) + ->map(static fn($property) => Aggregate\Property::of( + $property->name()->toString(), + Json::decode($property->content()->toString()), + )) + ->toSet(), + ), + ), + ) + ->toSet(), + ), + $directory + ->get(Name::of('collections')) + ->keep(Instance::of(Directory::class)) + ->map( + static fn($collections) => $collections + ->all() + ->keep(Instance::of(File::class)) + ->map( + static fn($collection) => Aggregate\Collection::of( + $collection->name()->toString(), + Set::of(...Json::decode($collection->content()->toString()))->map( + static fn($entity) => Set::of(...$entity)->map( + static fn($property) => Aggregate\Property::of( + $property[0], + $property[1], + ), + ), + ), + ), + ) + ->toSet(), + ), + )->map(static fn(Set $properties, Set $entities, Set $optionals, Set $collections) => Aggregate::of( + $id($directory), + $properties, + $entities, + $optionals, + $collections, + )); } /** diff --git a/src/Adapter/Filesystem/Encode.php b/src/Adapter/Filesystem/Encode.php index f09182e..f212a76 100644 --- a/src/Adapter/Filesystem/Encode.php +++ b/src/Adapter/Filesystem/Encode.php @@ -3,10 +3,15 @@ namespace Formal\ORM\Adapter\Filesystem; -use Formal\ORM\Raw\Aggregate; +use Formal\ORM\Raw\{ + Aggregate, + Diff, +}; use Innmind\Filesystem\{ + Directory, File, File\Content, + Name, }; use Innmind\Json\Json; @@ -19,70 +24,100 @@ private function __construct() { } - public function __invoke(Aggregate $data): File + public function __invoke(Aggregate|Diff $data): Directory { - return File::named( - $data->id()->value(), - Content::ofString(Json::encode([ - 'properties' => $data + return Directory::named($data->id()->value()) + ->add( + $data ->properties() - ->map(static fn($property) => [$property->name(), $property->value()]) - ->toList(), - 'entities' => $data + ->map(static fn($property) => File::named( + $property->name(), + Content::ofString(Json::encode($property->value())), + )) + ->reduce( + Directory::named('properties'), + static fn(Directory $properties, $property) => $properties->add($property), + ), + ) + ->add( + $data ->entities() ->map( - static fn($entity) => [ - $entity->name(), - $entity - ->properties() - ->map(static fn($property) => [$property->name(), $property->value()]) - ->toList(), - ], + static fn($entity) => $entity + ->properties() + ->map(static fn($property) => File::named( + $property->name(), + Content::ofString(Json::encode($property->value())), + )) + ->reduce( + Directory::named($entity->name()), + static fn(Directory $entity, $property) => $entity->add($property), + ), ) - ->toList(), - 'optionals' => $data + ->reduce( + Directory::named('entities'), + static fn(Directory $entities, $entity) => $entities->add($entity), + ), + ) + ->add( + $data ->optionals() ->map( - static fn($optional) => [ - $optional->name(), - $optional - ->properties() - ->map( - static fn($properties) => $properties - ->map(static fn($property) => [ - $property->name(), - $property->value(), - ]) - ->toList(), - ) - ->match( - static fn($properties) => $properties, - static fn() => null, - ), - ], + static fn($optional) => $optional + ->properties() + ->map( + static fn($properties) => $properties + ->map(static fn($property) => File::named( + $property->name(), + Content::ofString(Json::encode($property->value())), + )) + ->reduce( + Directory::named('just'), + static fn(Directory $properties, $property) => $properties->add($property), + ), + ) + ->match( + static fn($properties) => Directory::named($optional->name())->add($properties), + static fn() => Directory::named($optional->name())->remove(Name::of('just')), // erase previous data + ), ) - ->toList(), - 'collections' => $data + ->reduce( + Directory::named('optionals'), + static fn(Directory $optionals, $optional) => $optionals->add($optional), + ), + ) + ->add( + // Each collection is stored in a signle file to make sure no + // previous stored data is kept on the filesystem. Another + // solution would be to store each entity of the collection to a + // dedicated directory/file but there is currently no way to + // tell the filesystem to erase all files in a directory before + // persisting the new version. + $data ->collections() ->map( - static fn($collection) => [ + static fn($collection) => File::named( $collection->name(), - $collection - ->properties() - ->map( - static fn($properties) => $properties - ->map(static fn($property) => [ - $property->name(), - $property->value(), - ]) - ->toList(), - ) - ->toList(), - ], + Content::ofString(Json::encode( + $collection + ->properties() + ->map( + static fn($properties) => $properties + ->map(static fn($property) => [ + $property->name(), + $property->value(), + ]) + ->toList(), + ) + ->toList(), + )), + ), ) - ->toList(), - ])), - ); + ->reduce( + Directory::named('collections'), + static fn(Directory $collections, $collection) => $collections->add($collection), + ), + ); } /** diff --git a/src/Adapter/Filesystem/Repository.php b/src/Adapter/Filesystem/Repository.php index 17c7dd3..4f4b501 100644 --- a/src/Adapter/Filesystem/Repository.php +++ b/src/Adapter/Filesystem/Repository.php @@ -69,7 +69,7 @@ public function get(Aggregate\Id $id): Maybe return $this ->directory() ->get(Name::of($id->value())) - ->keep(Instance::of(File::class)) + ->keep(Instance::of(Directory::class)) ->flatMap(($this->decode)($id)); } @@ -93,13 +93,13 @@ public function add(Aggregate $data): void public function update(Diff $data): void { - $_ = $this - ->get($data->id()) - ->map(Apply::of($data)) - ->match( - $this->add(...), - static fn() => null, - ); + $this->transaction->mutate( + fn($adapter) => $adapter->add( + Directory::named($this->definition->name())->add( + ($this->encode)($data), + ), + ), + ); } public function remove(Aggregate\Id $id): void @@ -188,7 +188,7 @@ private function all(): Sequence return $this ->directory() ->all() - ->keep(Instance::of(File::class)) + ->keep(Instance::of(Directory::class)) ->flatMap(static fn($file) => $decode($file)->toSequence()); }