Skip to content

Commit

Permalink
Detect DBAL's number type
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Jan 6, 2025
1 parent 9f2b367 commit 5c59829
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 19 deletions.
8 changes: 7 additions & 1 deletion src/Mapping/DefaultTypedFieldMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Doctrine\ORM\Mapping;

use BackedEnum;
use BcMath\Number;
use DateInterval;
use DateTime;
use DateTimeImmutable;
Expand Down Expand Up @@ -40,7 +41,12 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
/** @param array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */
public function __construct(array $typedFieldMappings = [])
{
$this->typedFieldMappings = array_merge(self::DEFAULT_TYPED_FIELD_MAPPINGS, $typedFieldMappings);
$defaultMappings = self::DEFAULT_TYPED_FIELD_MAPPINGS;
if (defined(Types::class . '::NUMBER')) { // DBAL 4.3+
$defaultMappings[Number::class] = Types::NUMBER;
}

$this->typedFieldMappings = array_merge($defaultMappings, $typedFieldMappings);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions tests/Tests/Models/TypedProperties/UserTyped.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\Tests\Models\TypedProperties;

use BcMath\Number;
use DateInterval;
use DateTime;
use DateTimeImmutable;
Expand Down Expand Up @@ -54,6 +55,9 @@ class UserTyped
#[ORM\Embedded]
public Contact|null $contact = null;

#[ORM\Column(precision: 5, scale: 2)]
public Number|null $bodyHeight = null;

public static function loadMetadata(ClassMetadata $metadata): void
{
$metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);
Expand Down
7 changes: 7 additions & 0 deletions tests/Tests/ORM/Mapping/ClassMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
use function assert;
use function class_exists;
use function count;
use function defined;
use function serialize;
use function str_contains;
use function str_replace;
Expand Down Expand Up @@ -197,6 +198,12 @@ public function testFieldTypeFromReflection(): void
// float
$cm->mapField(['fieldName' => 'float']);
self::assertEquals('float', $cm->getTypeOfField('float'));

// number, requires DBAL 4.3+
if (defined(Types::class . '::NUMBER')) {
$cm->mapField(['fieldName' => 'bodyHeight']);
self::assertEquals('number', $cm->getTypeOfField('bodyHeight'));
}
}

#[TestGroup('GH10313')]
Expand Down
41 changes: 23 additions & 18 deletions tests/Tests/ORM/Mapping/TypedFieldMapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
use Doctrine\Tests\Models\TypedProperties\UserTyped;
use Doctrine\Tests\ORM\Mapping\TypedFieldMapper\CustomIntAsStringTypedFieldMapper;
use Doctrine\Tests\OrmTestCase;
use Generator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use ReflectionClass;

use function defined;

#[Group('GH10313')]
class TypedFieldMapperTest extends OrmTestCase
{
Expand All @@ -36,36 +39,38 @@ private static function chainTypedFieldMapper(): ChainTypedFieldMapper
/**
* Data Provider for NamingStrategy#classToTableName
*
* @return array<
* @return Generator<
* array{
* TypedFieldMapper,
* ReflectionClass,
* array{fieldName: string, enumType?: string, type?: mixed},
* array{fieldName: string, enumType?: string, type?: mixed}
* }>
*/
public static function dataFieldToMappedField(): array
public static function dataFieldToMappedField(): Generator
{
$reflectionClass = new ReflectionClass(UserTyped::class);

return [
// DefaultTypedFieldMapper
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::INTEGER]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateInterval'], ['fieldName' => 'dateInterval', 'type' => Types::DATEINTERVAL]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTime'], ['fieldName' => 'dateTime', 'type' => Types::DATETIME_MUTABLE]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTimeImmutable'], ['fieldName' => 'dateTimeImmutable', 'type' => Types::DATETIME_IMMUTABLE]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'array'], ['fieldName' => 'array', 'type' => Types::JSON]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'boolean'], ['fieldName' => 'boolean', 'type' => Types::BOOLEAN]],
[self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'float'], ['fieldName' => 'float', 'type' => Types::FLOAT]],
// DefaultTypedFieldMapper
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::INTEGER]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateInterval'], ['fieldName' => 'dateInterval', 'type' => Types::DATEINTERVAL]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTime'], ['fieldName' => 'dateTime', 'type' => Types::DATETIME_MUTABLE]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTimeImmutable'], ['fieldName' => 'dateTimeImmutable', 'type' => Types::DATETIME_IMMUTABLE]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'array'], ['fieldName' => 'array', 'type' => Types::JSON]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'boolean'], ['fieldName' => 'boolean', 'type' => Types::BOOLEAN]];
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'float'], ['fieldName' => 'float', 'type' => Types::FLOAT]];

if (defined(Types::class . '::NUMBER')) {
yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'bodyHeight'], ['fieldName' => 'bodyHeight', 'type' => Types::NUMBER]];
}

// CustomIntAsStringTypedFieldMapper
[self::customTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]],
// CustomIntAsStringTypedFieldMapper
yield [self::customTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]];

// ChainTypedFieldMapper
[self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]],
[self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]],
];
// ChainTypedFieldMapper
yield [self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]];
yield [self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]];
}

/**
Expand Down

0 comments on commit 5c59829

Please sign in to comment.