From a39aeb047c726ba951e421e02c125425bd1109e5 Mon Sep 17 00:00:00 2001 From: gam6itko Date: Tue, 8 Mar 2022 20:23:28 +0300 Subject: [PATCH 1/3] BoolTypecastGenerator - adds by default bool typecast for boolean fields --- src/Generator/BoolTypecastGenerator.php | 24 +++++++++ .../Generator/BoolTypecastGeneratorTest.php | 51 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/Generator/BoolTypecastGenerator.php create mode 100644 tests/Schema/Generator/BoolTypecastGeneratorTest.php diff --git a/src/Generator/BoolTypecastGenerator.php b/src/Generator/BoolTypecastGenerator.php new file mode 100644 index 0000000..3c7ee37 --- /dev/null +++ b/src/Generator/BoolTypecastGenerator.php @@ -0,0 +1,24 @@ +getFields() as $field) { + if ('boolean' === $field->getType() && !$field->hasTypecast()) { + $field->setTypecast('bool'); + } + } + } + + return $registry; + } +} diff --git a/tests/Schema/Generator/BoolTypecastGeneratorTest.php b/tests/Schema/Generator/BoolTypecastGeneratorTest.php new file mode 100644 index 0000000..5302e5e --- /dev/null +++ b/tests/Schema/Generator/BoolTypecastGeneratorTest.php @@ -0,0 +1,51 @@ +createMock(Registry::class); + $registry + ->expects(self::atLeastOnce()) + ->method('getFields') + ->willReturn($field); + $generator = new BoolTypecastGenerator(); + $generator->run($registry); + self::assertSame($expectedTypecast, $field->getTypecast()); + } + + public function dataSetTypecast(): iterable + { + yield [ + (new Field())->setType('boolean'), + 'bool', + ]; + + yield [ + (new Field())->setType('boolean')->setTypecast('foo'), + 'foo', + ]; + + yield [ + (new Field())->setType('bool'), + null, + ]; + + yield [ + (new Field())->setType('int'), + null, + ]; + } +} From e2a07c542bf30852454836c6a2cd516b7e0ea1e6 Mon Sep 17 00:00:00 2001 From: gam6itko Date: Thu, 10 Mar 2022 08:40:44 +0300 Subject: [PATCH 2/3] GenerateTypecast byField type --- src/Generator/BoolTypecastGenerator.php | 24 --------- src/Generator/GenerateTypecast.php | 24 +++++++-- .../Generator/TypecastGeneratorTest.php | 49 ++++++++++++++++++- 3 files changed, 69 insertions(+), 28 deletions(-) delete mode 100644 src/Generator/BoolTypecastGenerator.php diff --git a/src/Generator/BoolTypecastGenerator.php b/src/Generator/BoolTypecastGenerator.php deleted file mode 100644 index 3c7ee37..0000000 --- a/src/Generator/BoolTypecastGenerator.php +++ /dev/null @@ -1,24 +0,0 @@ -getFields() as $field) { - if ('boolean' === $field->getType() && !$field->hasTypecast()) { - $field->setTypecast('bool'); - } - } - } - - return $registry; - } -} diff --git a/src/Generator/GenerateTypecast.php b/src/Generator/GenerateTypecast.php index b532048..b1ba93b 100644 --- a/src/Generator/GenerateTypecast.php +++ b/src/Generator/GenerateTypecast.php @@ -4,10 +4,10 @@ namespace Cycle\Schema\Generator; +use Cycle\Database\Schema\AbstractColumn; use Cycle\Schema\Definition\Entity; use Cycle\Schema\GeneratorInterface; use Cycle\Schema\Registry; -use Cycle\Database\Schema\AbstractColumn; /** * Must be run after RenderTable. @@ -22,19 +22,37 @@ final class GenerateTypecast implements GeneratorInterface public function run(Registry $registry): Registry { foreach ($registry as $entity) { - $this->compute($registry, $entity); + $this->computeByFieldType($entity); + $this->computeByColumn($registry, $entity); } return $registry; } + private function computeByFieldType(Entity $entity): void + { + foreach ($entity->getFields() as $field) { + if ($field->hasTypecast()) { + continue; + } + + $field->setTypecast( + match ($field->getType()) { + 'bool', 'boolean' => 'bool', + 'int', 'integer' => 'int', + default => null + } + ); + } + } + /** * Automatically clarify column types based on table column types. * * @param Registry $registry * @param Entity $entity */ - protected function compute(Registry $registry, Entity $entity): void + protected function computeByColumn(Registry $registry, Entity $entity): void { if (!$registry->hasTable($entity)) { return; diff --git a/tests/Schema/Generator/TypecastGeneratorTest.php b/tests/Schema/Generator/TypecastGeneratorTest.php index 1c49bc4..b9ad612 100644 --- a/tests/Schema/Generator/TypecastGeneratorTest.php +++ b/tests/Schema/Generator/TypecastGeneratorTest.php @@ -6,6 +6,8 @@ use Cycle\ORM\Schema; use Cycle\Schema\Compiler; +use Cycle\Schema\Definition\Entity; +use Cycle\Schema\Definition\Field; use Cycle\Schema\Generator\GenerateTypecast; use Cycle\Schema\Generator\RenderTables; use Cycle\Schema\Registry; @@ -24,11 +26,56 @@ public function testCompiledUser(): void $c = new Compiler(); $schema = $c->compile($r, [new RenderTables(), new GenerateTypecast()]); - $this->assertSame('int', $schema['user'][Schema::TYPECAST]['p_id']); $this->assertSame('float', $schema['user'][Schema::TYPECAST]['p_balance']); $this->assertSame('datetime', $schema['user'][Schema::TYPECAST]['p_created_at']); $this->assertTrue(in_array($schema['user'][Schema::TYPECAST]['p_id'], ['int', 'bool'])); } + + /** + * @dataProvider dataBoolTypecast + */ + public function testBoolTypecast(Field $field, ?string $expectedTypecast): void + { + $entity = new Entity(); + $entity->setRole('user'); + $entity->setClass(User::class); + $entity->getFields()->set('field', $field->setColumn('field')); + + $r = new Registry($this->dbal); + $r->register($entity)->linkTable($entity, 'default', 'user'); + + $c = new Compiler(); + $c->compile($r, [new RenderTables(), new GenerateTypecast()]); + self::assertSame($expectedTypecast, $field->getTypecast()); + } + + public function dataBoolTypecast(): iterable + { + yield [ + (new Field())->setType('boolean'), + 'bool', + ]; + + yield [ + (new Field())->setType('bool'), + 'bool', + ]; + + yield [ + (new Field())->setType('int'), + 'int', + ]; + + yield [ + (new Field())->setType('integer'), + 'int', + ]; + + yield [ + (new Field())->setType('boolean')->setTypecast('foo'), + 'foo', + ]; + } } From 05415ba2d55552346cc569822be82f9af8f2f325 Mon Sep 17 00:00:00 2001 From: gam6itko Date: Fri, 29 Apr 2022 17:03:24 +0300 Subject: [PATCH 3/3] GenerateTypecast computeByClassPropertyType --- src/Generator/GenerateTypecast.php | 29 +++++++ tests/Schema/Fixtures/EntityForTypecast.php | 35 ++++++++ .../Generator/TypecastGeneratorTest.php | 84 +++++++++++++++---- 3 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 tests/Schema/Fixtures/EntityForTypecast.php diff --git a/src/Generator/GenerateTypecast.php b/src/Generator/GenerateTypecast.php index b1ba93b..ab35e94 100644 --- a/src/Generator/GenerateTypecast.php +++ b/src/Generator/GenerateTypecast.php @@ -22,6 +22,7 @@ final class GenerateTypecast implements GeneratorInterface public function run(Registry $registry): Registry { foreach ($registry as $entity) { + $this->computeByClassPropertyType($entity); $this->computeByFieldType($entity); $this->computeByColumn($registry, $entity); } @@ -29,6 +30,33 @@ public function run(Registry $registry): Registry return $registry; } + private function computeByClassPropertyType(Entity $entity): void + { + $refClass = new \ReflectionClass($entity->getClass()); + foreach ($entity->getFields() as $field) { + if ($field->hasTypecast()) { + continue; + } + if (!$refClass->hasProperty($field->getColumn())) { + continue; + } + + $refProp = $refClass->getProperty($field->getColumn()); + if (!$refProp->hasType() || !$refProp->getType()->isBuiltin()) { + continue; + } + + $field->setTypecast( + match ($refProp->getType()->getName()) { + 'bool' => 'bool', + 'int' => 'int', + 'string' => 'string', + default => null + } + ); + } + } + private function computeByFieldType(Entity $entity): void { foreach ($entity->getFields() as $field) { @@ -40,6 +68,7 @@ private function computeByFieldType(Entity $entity): void match ($field->getType()) { 'bool', 'boolean' => 'bool', 'int', 'integer' => 'int', + 'string' => 'string', default => null } ); diff --git a/tests/Schema/Fixtures/EntityForTypecast.php b/tests/Schema/Fixtures/EntityForTypecast.php new file mode 100644 index 0000000..2b14e8a --- /dev/null +++ b/tests/Schema/Fixtures/EntityForTypecast.php @@ -0,0 +1,35 @@ +setRole('user'); - $entity->setClass(User::class); - $entity->getFields()->set('field', $field->setColumn('field')); + $entity->setRole('entityForTypecast'); + $entity->setClass(\Cycle\Schema\Tests\Fixtures\EntityForTypecast::class); + $entity->getFields()->set($field->getColumn(), $field); $r = new Registry($this->dbal); - $r->register($entity)->linkTable($entity, 'default', 'user'); + $r->register($entity)->linkTable($entity, 'default', 'entityForTypecast'); $c = new Compiler(); $c->compile($r, [new RenderTables(), new GenerateTypecast()]); self::assertSame($expectedTypecast, $field->getTypecast()); } - public function dataBoolTypecast(): iterable + public function dataTypecast(): iterable { - yield [ - (new Field())->setType('boolean'), + // based on property type + yield 'int_integer' => [ + (new Field())->setType('integer')->setColumn('int_integer'), + 'int', + ]; + + yield 'int_tinyInteger' => [ + (new Field())->setType('tinyInteger')->setColumn('int_tinyInteger'), + 'int', + ]; + + yield 'int_bigInteger' => [ + (new Field())->setType('bigInteger')->setColumn('int_bigInteger'), + 'int', + ]; + + yield 'bool_boolean' => [ + (new Field())->setType('boolean')->setColumn('bool_boolean'), 'bool', ]; - yield [ - (new Field())->setType('bool'), + yield 'string_string' => [ + (new Field())->setType('string')->setColumn('string_string'), + 'string', + ]; + + // based on orm type + yield '_integer' => [ + (new Field())->setType('integer')->setColumn('_integer'), + 'int', + ]; + + yield '_boolean' => [ + (new Field())->setType('boolean')->setColumn('_boolean'), 'bool', ]; - yield [ - (new Field())->setType('int'), + yield '_string' => [ + (new Field())->setType('string')->setColumn('_string'), + 'string', + ]; + + // based on property type + yield 'int_boolean' => [ + (new Field())->setType('boolean')->setColumn('int_boolean'), 'int', ]; - yield [ - (new Field())->setType('integer'), + yield 'int_string' => [ + (new Field())->setType('string')->setColumn('int_string'), 'int', ]; - yield [ - (new Field())->setType('boolean')->setTypecast('foo'), - 'foo', + yield 'bool_integer' => [ + (new Field())->setType('integer')->setColumn('bool_integer'), + 'bool', + ]; + + yield 'bool_string' => [ + (new Field())->setType('string')->setColumn('bool_string'), + 'bool', + ]; + + yield 'string_boolean' => [ + (new Field())->setType('boolean')->setColumn('string_boolean'), + 'string', + ]; + + yield 'string_integer' => [ + (new Field())->setType('integer')->setColumn('string_integer'), + 'string', ]; } }