Skip to content

Commit

Permalink
allow to convert a type to a floating point
Browse files Browse the repository at this point in the history
  • Loading branch information
Baptouuuu committed Oct 31, 2024
1 parent 0b1ed42 commit 80be049
Show file tree
Hide file tree
Showing 23 changed files with 167 additions and 51 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Added

- Allow to convert types to floating points.

### Fixed

- Cross matching on an aggregate or entity property.
Expand Down
22 changes: 22 additions & 0 deletions fixtures/CreatedAt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
declare(strict_types = 1);

namespace Fixtures\Formal\ORM;

/**
* @psalm-immutable
*/
final class CreatedAt
{
private float $value;

public function __construct(float $value)
{
$this->value = $value;
}

public function toFloat(): float
{
return $this->value;
}
}
65 changes: 65 additions & 0 deletions fixtures/CreatedAtType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
declare(strict_types = 1);

namespace Fixtures\Formal\ORM;

use Fixtures\Formal\ORM\CreatedAt;
use Formal\ORM\Adapter\Elasticsearch\ElasticsearchType;
use Formal\ORM\Adapter\SQL\SQLType;
use Formal\ORM\Definition\{
Type,
Types,
};
use Formal\AccessLayer\Table\Column\Type as Definition;
use Innmind\Type\{
Type as Concrete,
ClassName,
};
use Innmind\Immutable\Maybe;

/**
* @psalm-immutable
* @implements Type<CreatedAt>
*/
final class CreatedAtType implements Type, SQLType, ElasticsearchType
{
private function __construct()
{
}

/**
* @psalm-pure
*
* @return Maybe<self>
*/
public static function of(Types $types, Concrete $type): Maybe
{
return Maybe::just($type)
->filter(static fn($type) => $type->accepts(ClassName::of(CreatedAt::class)))
->map(static fn() => new self);
}

public function elasticsearchType(): array
{
return ['type' => 'double'];
}

public function sqlType(): Definition
{
return Definition::decimal(65, 2);
}

public function normalize(mixed $value): null|string|int|float|bool
{
return $value->toFloat();
}

public function denormalize(null|string|int|float|bool $value): mixed
{
if (!\is_float($value)) {
throw new \LogicException("'$value' is not a float");
}

return new CreatedAt($value);
}
}
4 changes: 2 additions & 2 deletions fixtures/SortableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ public function elasticsearchType(): array
return ['type' => 'keyword'];
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value->toString();
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
if (!\is_string($value)) {
throw new \LogicException("'$value' is not a string");
Expand Down
9 changes: 9 additions & 0 deletions fixtures/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ final class User
/** @var Id<self> */
private Id $id;
private PointInTime $createdAt;
// To show by default floats are not supported on purpose
private float $createdAtFloat;
private CreatedAt $wrappedCreatedAt;
private ?string $name;
/** @var Maybe<Str> */
#[Contains(Str::class)]
Expand Down Expand Up @@ -66,6 +69,12 @@ private function __construct(
) {
$this->id = $id;
$this->createdAt = $createdAt;
$this->createdAtFloat = (float) \sprintf(
'%s.%s',
$createdAt->second()->toInt(),
$createdAt->millisecond()->toInt(),
);
$this->wrappedCreatedAt = new CreatedAt($this->createdAtFloat);
$this->name = $name;
$this->nameStr = Maybe::of($name)->map(Str::of(...));
$this->sortableName = Sortable::of($name);
Expand Down
9 changes: 8 additions & 1 deletion proofs/adapter/elasticsearch/mapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
Earth\Clock,
PointInTime,
};
use Fixtures\Formal\ORM\User;
use Fixtures\Formal\ORM\{
User,
CreatedAtType,
};

return static function() {
yield test(
Expand All @@ -23,6 +26,7 @@ static function($assert) {
PointInTime::class,
Type\PointInTimeType::new(new Clock),
),
CreatedAtType::of(...),
));

$mapping = Mapping::new()($aggregates->get(User::class));
Expand All @@ -36,6 +40,9 @@ static function($assert) {
'createdAt' => [
'type' => 'text',
],
'wrappedCreatedAt' => [
'type' => 'double',
],
'name' => [
'type' => 'text',
],
Expand Down
13 changes: 9 additions & 4 deletions proofs/adapter/sql/showCreateTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
Earth\Clock,
PointInTime,
};
use Fixtures\Formal\ORM\User;
use Fixtures\Formal\ORM\{
User,
CreatedAtType,
};

return static function() {
yield test(
Expand All @@ -25,6 +28,7 @@ static function($assert) {
PointInTime::class,
Type\PointInTimeType::new(new Clock),
),
CreatedAtType::of(...),
)),
);

Expand All @@ -35,7 +39,7 @@ static function($assert) {
$assert
->expected([
<<<SQL
CREATE TABLE `user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
CREATE TABLE `user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `wrappedCreatedAt` decimal(65, 2) NOT NULL , `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
SQL,
<<<SQL
CREATE TABLE `user_mainAddress` (`aggregateId` char(36) NOT NULL COMMENT 'UUID', `value` longtext NOT NULL COMMENT 'TODO adjust the type depending on your use case', `id` bigint DEFAULT NULL COMMENT 'TODO Adjust the size depending on your use case', `enabled` tinyint(1) NOT NULL COMMENT 'Boolean', CONSTRAINT `FK_user_mainAddress` FOREIGN KEY (`aggregateId`) REFERENCES `user`(`id`) ON DELETE CASCADE, UNIQUE (`aggregateId`))
Expand All @@ -62,7 +66,7 @@ static function($assert) {
$assert
->expected([
<<<SQL
CREATE TABLE IF NOT EXISTS `user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
CREATE TABLE IF NOT EXISTS `user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `wrappedCreatedAt` decimal(65, 2) NOT NULL , `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
SQL,
<<<SQL
CREATE TABLE IF NOT EXISTS `user_mainAddress` (`aggregateId` char(36) NOT NULL COMMENT 'UUID', `value` longtext NOT NULL COMMENT 'TODO adjust the type depending on your use case', `id` bigint DEFAULT NULL COMMENT 'TODO Adjust the size depending on your use case', `enabled` tinyint(1) NOT NULL COMMENT 'Boolean', CONSTRAINT `FK_user_mainAddress` FOREIGN KEY (`aggregateId`) REFERENCES `user`(`id`) ON DELETE CASCADE, UNIQUE (`aggregateId`))
Expand Down Expand Up @@ -93,6 +97,7 @@ static function($assert) {
PointInTime::class,
Type\PointInTimeType::new(new Clock),
),
CreatedAtType::of(...),
))->mapName(static fn($string) => match ($string) {
User::class => 'some_user',
}),
Expand All @@ -108,7 +113,7 @@ static function($assert) {
$assert
->expected(
<<<SQL
CREATE TABLE `some_user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
CREATE TABLE `some_user` (`id` char(36) NOT NULL COMMENT 'UUID', `createdAt` char(32) NOT NULL COMMENT 'Date with timezone down to the microsecond', `wrappedCreatedAt` decimal(65, 2) NOT NULL , `name` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `nameStr` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', `role` longtext DEFAULT NULL COMMENT 'TODO adjust the type depending on your use case', PRIMARY KEY (`id`))
SQL,
)
->in($queries);
Expand Down
4 changes: 4 additions & 0 deletions proofs/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
User,
Random,
SortableType,
CreatedAtType,
};
use Properties\Formal\ORM\{
Properties,
Expand Down Expand Up @@ -87,6 +88,7 @@ static function($assert) {
Aggregates::of(Types::of(
Type\PointInTimeType::of(new Clock),
SortableType::of(...),
CreateAtType::of(...),
)),
)),
)->tag(Storage::filesystem);
Expand All @@ -102,6 +104,7 @@ static function($assert) {
Type\PointInTimeType::new(new Clock),
),
SortableType::of(...),
CreateAtType::of(...),
)),
)),
)
Expand All @@ -116,6 +119,7 @@ static function($assert) {
Type\PointInTimeType::new($os->clock()),
),
SortableType::of(...),
CreateAtType::of(...),
));

$sql = static function(Url $dsn, string $driver) use ($os, $aggregates) {
Expand Down
16 changes: 8 additions & 8 deletions src/Adapter/Filesystem/Fold.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,19 @@ public static function of(Definition $definition): self
}

/**
* @return callable(null|string|int|bool): bool
* @return callable(null|string|int|float|bool): bool
*/
private function filter(Comparator $specification): callable
{
/** @psalm-suppress MixedArgument */
return match ($specification->sign()) {
Sign::equality => static fn(null|string|int|bool $value): bool => $value === $specification->value(),
Sign::lessThan => static fn(null|string|int|bool $value): bool => $value < $specification->value(),
Sign::moreThan => static fn(null|string|int|bool $value): bool => $value > $specification->value(),
Sign::startsWith => static fn(null|string|int|bool $value): bool => \is_string($value) && \str_starts_with($value, $specification->value()),
Sign::endsWith => static fn(null|string|int|bool $value): bool => \is_string($value) && \str_ends_with($value, $specification->value()),
Sign::contains => static fn(null|string|int|bool $value): bool => \is_string($value) && \str_contains($value, $specification->value()),
Sign::in => static fn(null|string|int|bool $value): bool => \in_array($value, $specification->value(), true),
Sign::equality => static fn(null|string|int|float|bool $value): bool => $value === $specification->value(),
Sign::lessThan => static fn(null|string|int|float|bool $value): bool => $value < $specification->value(),
Sign::moreThan => static fn(null|string|int|float|bool $value): bool => $value > $specification->value(),
Sign::startsWith => static fn(null|string|int|float|bool $value): bool => \is_string($value) && \str_starts_with($value, $specification->value()),
Sign::endsWith => static fn(null|string|int|float|bool $value): bool => \is_string($value) && \str_ends_with($value, $specification->value()),
Sign::contains => static fn(null|string|int|float|bool $value): bool => \is_string($value) && \str_contains($value, $specification->value()),
Sign::in => static fn(null|string|int|float|bool $value): bool => \in_array($value, $specification->value(), true),
};
}

Expand Down
4 changes: 2 additions & 2 deletions src/Adapter/Filesystem/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ public function fetch(

if ($sort) {
$compare = match ($sort->direction()) {
Sort::asc => static fn(null|string|int|bool $a, null|string|int|bool $b) => $a <=> $b,
Sort::desc => static fn(null|string|int|bool $a, null|string|int|bool $b) => $b <=> $a,
Sort::asc => static fn(null|string|int|float|bool $a, null|string|int|float|bool $b) => $a <=> $b,
Sort::desc => static fn(null|string|int|float|bool $a, null|string|int|float|bool $b) => $b <=> $a,
};
$pluck = match (true) {
$sort instanceof Sort\Property => static fn(Aggregate $x): mixed => $x
Expand Down
4 changes: 2 additions & 2 deletions src/Definition/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ interface Type
/**
* @param D $value
*/
public function normalize(mixed $value): null|string|int|bool;
public function normalize(mixed $value): null|string|int|float|bool;

/**
* @return D
*/
public function denormalize(null|string|int|bool $value): mixed;
public function denormalize(null|string|int|float|bool $value): mixed;
}
4 changes: 2 additions & 2 deletions src/Definition/Type/BoolType.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public static function of(Types $types, Concrete $type): Maybe
->map(static fn() => new self);
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value;
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
return match ($value) {
0 => false,
Expand Down
4 changes: 2 additions & 2 deletions src/Definition/Type/EnumType.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ public static function of(Types $types, Concrete $type): Maybe
->map(static fn($type) => new self($type->toString()));
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value->name;
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
foreach ($this->class::cases() as $case) {
if ($case->name === $value) {
Expand Down
4 changes: 2 additions & 2 deletions src/Definition/Type/IdType.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public static function of(Types $types, Concrete $type): Maybe
->map(static fn() => new self);
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value->toString();
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
if (!\is_string($value)) {
throw new \LogicException("'$value' is not a string");
Expand Down
4 changes: 2 additions & 2 deletions src/Definition/Type/IntType.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public static function of(Types $types, Concrete $type): Maybe
->map(static fn() => new self);
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value;
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
if (!\is_int($value)) {
throw new \LogicException("'$value' is not an integer");
Expand Down
4 changes: 2 additions & 2 deletions src/Definition/Type/MaybeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ public function inner(): Type
return $this->inner;
}

public function normalize(mixed $value): null|string|int|bool
public function normalize(mixed $value): null|string|int|float|bool
{
return $value->match(
$this->inner->normalize(...),
static fn() => null,
);
}

public function denormalize(null|string|int|bool $value): mixed
public function denormalize(null|string|int|float|bool $value): mixed
{
return Maybe::of($value)->map($this->inner->denormalize(...));
}
Expand Down
Loading

0 comments on commit 80be049

Please sign in to comment.