Skip to content

Commit 5f18353

Browse files
authored
[FRAM-182] Allow to define column type different from property type (#102)
* feat: Allow to define column type different from property type (#FRAM-182) * ci: fix psalm, doc: update changelog
1 parent 69144e8 commit 5f18353

10 files changed

Lines changed: 125 additions & 2 deletions

File tree

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
v2.3.0
2+
------
3+
4+
* Feat: Allow to explicitly declare the type of the column in the schema using `FieldBuilder::storedAs()`
5+
16
v2.2.0
27
------
38

src/Mapper/Builder/FieldBuilder.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* customSchemaOptions?: array,
4242
* platformOptions?: array,
4343
* columnDefinition?: string,
44+
* storageType?: string,
4445
* virtual?: bool
4546
* }
4647
*
@@ -607,6 +608,25 @@ public function fixed(bool $flag = true)
607608
return $this;
608609
}
609610

611+
/**
612+
* Define the actual type used to store the data.
613+
*
614+
* When provided, the schema manager will use this type instead of the default one,
615+
* the default type will only be used to parse or normalize the value.
616+
*
617+
* @param string $type The type name. Should be a constant of TypeInterface.
618+
*
619+
* @return $this
620+
*
621+
* @see TypeInterface::* constants
622+
*/
623+
public function storedAs(string $type)
624+
{
625+
$this->fields[$this->current]['storageType'] = $type;
626+
627+
return $this;
628+
}
629+
610630
/**
611631
* Declare the column as JSON type instead of TEXT.
612632
*

src/Mapper/Metadata.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* unsigned?: bool,
3131
* unique?: bool|string,
3232
* class?: class-string,
33+
* storageType?: string,
3334
* virtual?: bool
3435
* }
3536
*

src/Schema/Adapter/Metadata/MetadataColumn.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,18 @@ public function name(): string
4848
*/
4949
public function type(): PlatformTypeInterface
5050
{
51-
return $this->types->native($this->metadata['type']);
51+
return isset($this->metadata['storageType'])
52+
? $this->types->native($this->metadata['storageType'])
53+
: $this->types->native($this->metadata['type'])
54+
;
5255
}
5356

5457
/**
5558
* {@inheritdoc}
5659
*/
5760
public function defaultValue()
5861
{
59-
return $this->type()->toDatabase($this->metadata['default']);
62+
return $this->types->native($this->metadata['type'])->toDatabase($this->metadata['default']);
6063
}
6164

6265
/**

tests/CRUDTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,4 +542,25 @@ public function test_custom_nullable_type()
542542
$this->assertEquals('SELECT t0.* FROM my_custom_nullable t0 WHERE t0.foo IN (?,?)', $query->toSql());
543543
$this->assertSame(['0', 'bar'], $query->getBindings());
544544
}
545+
546+
public function test_with_custom_storage_type()
547+
{
548+
$this->pack()->declareEntity(EntityWithCustomStorageType::class);
549+
$entity = new EntityWithCustomStorageType([
550+
'id' => 42,
551+
'name' => 'foo',
552+
'value' => ['foo', 'bar', 'baz'],
553+
'enabled' => true,
554+
]);
555+
$entity->insert();
556+
557+
$this->assertEquals($entity, EntityWithCustomStorageType::refresh($entity));
558+
559+
$this->assertEquals([[
560+
'id' => '42.0',
561+
'name' => 'foo',
562+
'value' => ',foo,bar,baz,',
563+
'enabled' => '1',
564+
]], EntityWithCustomStorageType::repository()->builder()->execute()->all());
565+
}
545566
}

tests/Mapper/Builder/FieldBuiderTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Bdf\Prime\PolymorphSubA;
66
use Bdf\Prime\PolymorphSubB;
7+
use Bdf\Prime\Types\TypeInterface;
78
use PHPUnit\Framework\TestCase;
89

910
/**
@@ -247,6 +248,18 @@ public function test_add_precision()
247248
$this->assertSame(2, $builder['name']['scale']);
248249
}
249250

251+
/**
252+
*
253+
*/
254+
public function test_storedAs()
255+
{
256+
$builder = new FieldBuilder();
257+
258+
$builder->string('name')->storedAs(TypeInterface::JSON);
259+
260+
$this->assertSame('json', $builder['name']['storageType']);
261+
}
262+
250263
/**
251264
*
252265
*/

tests/Schema/Adapter/Metadata/MetadataColumnTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Bdf\Prime\Schema\Adapter\Metadata;
44

55
use Bdf\Prime\Document;
6+
use Bdf\Prime\EntityWithCustomStorageType;
67
use Bdf\Prime\Faction;
78
use Bdf\Prime\Mapper\Metadata;
89
use Bdf\Prime\Platform\PlatformInterface;
@@ -132,4 +133,13 @@ public function test_defaultValue_must_be_converted_to_db_value()
132133

133134
$this->assertSame(1, $column->defaultValue());
134135
}
136+
137+
public function test_custom_storage_type()
138+
{
139+
$this->metadata = EntityWithCustomStorageType::repository()->mapper()->metadata();
140+
141+
$column = new MetadataColumn($this->metadata->attributes['enabled'], $this->types);
142+
143+
$this->assertEquals($this->types->get(TypeInterface::INTEGER), $column->type());
144+
}
135145
}

tests/Schema/ResolverTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Bdf\Prime\CrossConnectionSequenceEntity;
66
use Bdf\Prime\Customer;
7+
use Bdf\Prime\EntityWithCustomStorageType;
78
use Bdf\Prime\EntityWithIndex;
89
use Bdf\Prime\Faction;
910
use Bdf\Prime\Prime;
@@ -237,4 +238,10 @@ public function test_diff_with_legacy_indexes_format_without_diff_should_return_
237238
$resolver->migrate();
238239
$this->assertEmpty($resolver->diff());
239240
}
241+
242+
public function test_custom_storage_type()
243+
{
244+
$resolver = EntityWithCustomStorageType::repository()->schema();
245+
$this->assertEquals(['CREATE TABLE entity_custom_storage_type (id DOUBLE PRECISION NOT NULL, name BLOB NOT NULL, value VARCHAR(32) NOT NULL, enabled INTEGER NOT NULL)'], $resolver->diff());
246+
}
240247
}

tests/_files/TestEntities.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
require_once __DIR__.'/entity_with_custom_nullable_type.php';
2121
require_once __DIR__.'/null_relation_inheritance.php';
2222
require_once __DIR__.'/json_entities.php';
23+
require_once __DIR__.'/entity_custom_storage_type.php';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bdf\Prime;
4+
5+
use Bdf\Prime\Entity\Model;
6+
use Bdf\Prime\Mapper\Builder\FieldBuilder;
7+
use Bdf\Prime\Mapper\Mapper;
8+
use Bdf\Prime\Types\TypeInterface;
9+
10+
class EntityWithCustomStorageType extends Model
11+
{
12+
public ?int $id;
13+
public ?string $name;
14+
public array $value = [];
15+
public bool $enabled = false;
16+
17+
public function __construct(array $data = [])
18+
{
19+
$this->import($data);
20+
}
21+
}
22+
23+
class EntityWithCustomStorageTypeMapper extends Mapper
24+
{
25+
public function schema(): array
26+
{
27+
return [
28+
'connection' => 'test',
29+
'table' => 'entity_custom_storage_type',
30+
];
31+
}
32+
33+
public function buildFields(FieldBuilder $builder): void
34+
{
35+
$builder
36+
->integer('id')->autoincrement()->storedAs(TypeInterface::DOUBLE)
37+
->string('name')->storedAs(TypeInterface::BLOB)
38+
->simpleArray('value')->storedAs(TypeInterface::STRING)->length(32)
39+
->boolean('enabled')->storedAs(TypeInterface::INTEGER)
40+
;
41+
}
42+
}

0 commit comments

Comments
 (0)