Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BoolTypecastGenerator - adds by default bool typecast for boolean fields #56

Open
wants to merge 3 commits into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 50 additions & 3 deletions src/Generator/GenerateTypecast.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -22,19 +22,66 @@ final class GenerateTypecast implements GeneratorInterface
public function run(Registry $registry): Registry
{
foreach ($registry as $entity) {
$this->compute($registry, $entity);
$this->computeByClassPropertyType($entity);
$this->computeByFieldType($entity);
$this->computeByColumn($registry, $entity);
}

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) {
if ($field->hasTypecast()) {
continue;
}

$field->setTypecast(
match ($field->getType()) {
'bool', 'boolean' => 'bool',
'int', 'integer' => 'int',
'string' => 'string',
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;
Expand Down
35 changes: 35 additions & 0 deletions tests/Schema/Fixtures/EntityForTypecast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Fixtures;

class EntityForTypecast
{
public $id;

public int $int_integer;

public int $int_tinyInteger;

public int $int_bigInteger;

public bool $bool_boolean;

public string $string_string;

public $_integer;

public $_boolean;

public $_string;

public int $int_boolean;
public int $int_string;

public bool $bool_integer;
public bool $bool_string;

public string $string_boolean;
public string $string_integer;
}
51 changes: 51 additions & 0 deletions tests/Schema/Generator/BoolTypecastGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Generator;

use Cycle\Schema\Definition\Field;
use Cycle\Schema\Generator\BoolTypecastGenerator;
use Cycle\Schema\Registry;
use PHPUnit\Framework\TestCase;

class BoolTypecastGeneratorTest extends TestCase
{
/**
* @depends dataSetTypecast
*/
public function testSetTypecast(Field $field, ?string $expectedTypecast)
{
$registry = $this->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,
];
}
}
97 changes: 96 additions & 1 deletion tests/Schema/Generator/TypecastGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,11 +26,104 @@ 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 dataTypecast
*/
public function testTypecast(Field $field, ?string $expectedTypecast): void
{
$entity = new Entity();
$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', 'entityForTypecast');

$c = new Compiler();
$c->compile($r, [new RenderTables(), new GenerateTypecast()]);
self::assertSame($expectedTypecast, $field->getTypecast());
}

public function dataTypecast(): iterable
{
// 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 '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 '_string' => [
(new Field())->setType('string')->setColumn('_string'),
'string',
];

// based on property type
yield 'int_boolean' => [
(new Field())->setType('boolean')->setColumn('int_boolean'),
'int',
];

yield 'int_string' => [
(new Field())->setType('string')->setColumn('int_string'),
'int',
];

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',
];
}
}