Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[PropertyInfo] DoctrineExtractor incompatible type resolution with DBAL 4 or ORM 2.19 #54418

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

Closed
llupa opened this issue Mar 27, 2024 · 4 comments
Labels
Bug Good first issue Ideal for your first contribution! (some Symfony experience may be required) PropertyInfo Status: Needs Review

Comments

@llupa
Copy link
Contributor

llupa commented Mar 27, 2024

Symfony version(s) affected

7.0.3

Description

Preface

With the recent release of Doctrine ORM 3 and DBAL 4 there has been a major change in at least one column type.

bigint now reads:

Maps and converts 8-byte integer values.
...

Values retrieved from the database are always converted to PHP's integer type if they are within PHP's integer range
or string if they aren't.

Additionally, if you did not yet make the jump to ORM 3/DBAL 4 and are using ORM 2.19 which acts like Sf .4 and the
next major .0 you can use int typehints in your PHP code with a bigint column type if you go for a string|int
union. You can read more about it in this issue: Invalid field error reported for bigint DBAL type.

Another case would be if you know that your project's tech will always rely on int within your PHP_INT_MAX you could
also override the default bigint type and bind it to int.

This is also dependent of your DB engine too, but generally speaking, all of the above are true for pgsql. How one goes about handling this Doctrine hiccup is up to developer's choice and from my POV any of the solutions that can be used for Doctrine is fine.

Symfony

Let's assume that you are using one of the methods to have Doctrine happy but without jeopardising your project. If your project is in any capacity in need of denormalizing request data into PHP objects this is the next step that breaks.

At one point, for objects, the AbstractObjectNormalizer::getTypes() is called, which in turn makes use of
PropertyInfoExtractor to resolve the Type of a bigint property that is in some way type-hinted as int and request data will use int values too.

DoctrineExtractor::getTypes() will internally try to get the PHP type of bigint which for now is always going to map
it to string.

// at about line ~250
return match ($doctrineType) {
    Types::SMALLINT,
    Types::INTEGER => Type::BUILTIN_TYPE_INT,
    Types::FLOAT => Type::BUILTIN_TYPE_FLOAT,
    Types::BIGINT, // <- here
    Types::STRING,
    Types::TEXT,
    Types::GUID,
    Types::DECIMAL => Type::BUILTIN_TYPE_STRING, // <- mapped to string
    // ...

This causes a gate-keeping effect with the following exception message:

The type of the "bigint" attribute for class "IHaveNumbers" must be one of "string" ("int" given).

For this there are some possible ways to go about it.

A) You can use DISABLE_TYPE_ENFORCEMENT on your context and the value is used as is, letting it be handled on code level
which may result in \Error issues.
B) Switch your property to string only and add more cast juggling.
C) Switch your property to string and also reflect this in your general API so that request data is now only string.
D) Update / Decorate DoctrineExtractor with the following hack solution:

// new block
if (Types::BIGINT === $typeOfField) {
    return [
        new Type(Type::BUILTIN_TYPE_INT, $nullable),
        new Type(Type::BUILTIN_TYPE_STRING, $nullable),
    ];
}
// continue how it used to be
if (!$builtinType = $this->getPhpType($typeOfField)) {
    return null;
}

I went for option D because I really want to have bigint typed to int and not to string as I have a project that
only delivers an API with API Platform. So keeping my JSON Schema consistent is top priority.

It works, but I am not sure if it is a) a solution and b) a preferred solution.

How to reproduce

I can create a simple project if the description above is not enough. The easiest way would be to:

  • use API Platform with one resource and pgsql for storage.
  • use orm 3 and dbal 4
  • add 1 property with bigint
  • send a JSON body with int values in your property
  • The type of the "bigint" attribute for class "IHaveNumbers" must be one of "string" ("int" given).

Possible Solution

In case DoctrineExtractor cannot be updated one can also play around with attributes. Below I have my example in my project:

#[ORM\Column(type: Types::BIGINT)]
#[ApiProperty(
    jsonSchemaContext: ['type' => 'integer'],
    extraProperties: [SchemaPropertyMetadataFactory::JSON_SCHEMA_USER_DEFINED => true]
)]
#[Context(denormalizationContext: [AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true])]
#[Assert\Type(type: 'int')]
#[Assert\Positive]
#[Assert\Range(max: 12_345_678_901)]
private $bigInt;

Additional Context

No response

@stof
Copy link
Member

stof commented Mar 29, 2024

DoctrineExtractor should be updated to account for the new DBAL 4 behavior for the Bigint type. PRs are welcome.

@stof stof added the Good first issue Ideal for your first contribution! (some Symfony experience may be required) label Mar 29, 2024
@llupa
Copy link
Contributor Author

llupa commented Mar 29, 2024

I don't mind opening a PR with my initial idea, contributors can nuke me there with their feedback 😄

The main problem is this fallback behavior between types that cannot be correctly reflected with getTypes. In cases when a developer is using union (string|int) this will work, but if they use int only, third party libraries check if the field is a union and else into an intersection, or the other way around (it is neither).

In any case, I am cheating a bit because I am using this issue as a way to catalog my findings into this since I spent ~3 to ~5 hours trying to make Doctrine + Sf Serializer + Api Platform happy 😅

fabpot added a commit that referenced this issue May 17, 2024
…INT type (llupa)

This PR was squashed before being merged into the 5.4 branch.

Discussion
----------

[PropertyInfo] Update DoctrineExtractor for new DBAL 4 BIGINT type

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | not sure
| Deprecations? | no
| Issues        | Fix #54418
| License       | MIT

## Additional Considerations

This issue looks pretty straight forward, but it has had me running in circles not being sure how to exactly interpret it. The new return type to make it work with DBAL 4 is _fine_, but it is neither an intersection nor a union type, which **will** cause trouble for other libs if they do not explicitly check each.

There is not easy way to get which DBAL version is the extractor is running against, so trying to optimize the flow is _tricky_. I am opening this PR to have a starting point in the hopes that maintainers of this package have more historical context than me.

I have tried to document as much as possible about this in the issue linked above. 🍻

Commits
-------

92e54ac [PropertyInfo] Update DoctrineExtractor for new DBAL 4 BIGINT type
@fabpot fabpot closed this as completed May 17, 2024
@sophie-la-li
Copy link

sophie-la-li commented Jul 23, 2024

I have the same issue regardless of the merged fix. Having a property defined like

/** @ORM\Column(type="decimal", precision=10, scale=2) */
protected float $revenue = 0.0;

Denormalizing an array to this object

 $this->serializer->denormalize($inputArray, AnyObject::class);

Results in

 The type of the "revenue" attribute for class "AnyObject" must be one of "string" ("float" given).

Symfony Version is 6.4.9

$typeOfField is "decimal" in my case.

@xabbuh
Copy link
Member

xabbuh commented Jul 23, 2024

@sophie-kuehn Please open a new issue and provide a small example application that allows to reproduce it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Good first issue Ideal for your first contribution! (some Symfony experience may be required) PropertyInfo Status: Needs Review
Projects
None yet
Development

No branches or pull requests

6 participants