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

Custom DBAL Type Ignored During Hydration: TypeError When Using Collection in Entity #6667

Open
pizgariu opened this issue Dec 24, 2024 · 0 comments

Comments

@pizgariu
Copy link

Bug Report

Q A
Version 2.12.2
Previous Version if the bug is a regression Not applicable

Summary

When using a custom DBAL type that converts JSON data into a Doctrine\Common\Collections\Collection (e.g., ArrayCollection), Doctrine fails to properly handle hydration. Despite the type being registered and used in the entity, the convertToPHPValue method of the custom type is ignored during hydration, resulting in a TypeError. Doctrine tries to assign an array to a property typed as Collection, which leads to the error.

Current behavior

Doctrine does not use the custom DBAL type during hydration. Instead, it attempts to assign a raw array to the entity property, ignoring the expected conversion to Collection.

Expected behavior

Doctrine should respect the custom DBAL type and correctly convert the JSON data into the expected Collection instance during hydration.

How to reproduce

Provide a custom DBAL type:

namespace App\DBAL\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Doctrine\Common\Collections\ArrayCollection;

class JsonCollectionType extends Type
{
    const JSON_COLLECTION = 'json_collection';

    public function getSQLDeclaration(array $column, AbstractPlatform $platform)
    {
        return $platform->getJsonTypeDeclarationSQL($column);
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        $data = json_decode($value, true);

        return new ArrayCollection($data ?? []);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if ($value instanceof ArrayCollection) {
            $value = $value->toArray();
        }

        return json_encode($value);
    }

    public function getName(): string
    {
        return self::JSON_COLLECTION;
    }
}

Register the type:

use Doctrine\DBAL\Types\Type;

if (!Type::hasType('json_collection')) {
    Type::addType('json_collection', \App\DBAL\Types\JsonCollectionType::class);
}

Define an entity:

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;

#[ORM\Entity]
class Product
{
    #[ORM\Column(type: "json_collection")]
    private Collection $paymentMethodsUuids;

    public function __construct()
    {
        $this->paymentMethodsUuids = new ArrayCollection();
    }

    public function getPaymentMethodsUuids(): Collection
    {
        return $this->paymentMethodsUuids;
    }

    public function setPaymentMethodsUuids(Collection $paymentMethodsUuids): void
    {
        $this->paymentMethodsUuids = $paymentMethodsUuids;
    }
}

When trying to hydrate this entity, you get the following error:

TypeError: Typed property App\Entity\Product::$paymentMethodsUuids must be an instance of Doctrine\Common\Collections\Collection, array used

Stacktrace

Click to expand stacktrace
{
    "errors": [
        {
            "class": "TypeError",
            "message": "Typed property Entity::$paymentMethodsUuids must be an instance of Collection, array used",
            "code": 0,
            "file": "/vendor/doctrine/persistence/src/Persistence/Reflection/TypedNoDefaultReflectionPropertyBase.php",
            "line": 64,
            "stacktrace": "#0 /vendor/doctrine/persistence/src/Persistence/Reflection/TypedNoDefaultReflectionPropertyBase.php(64): ReflectionProperty->setValue()\n#1 /vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(2745): Persistence\\Reflection\\TypedNoDefaultReflectionProperty->setValue()\n#2 /vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(255): ORM\\UnitOfWork->createEntity()\n#3 /vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(481): ORM\\Internal\\Hydration\\ObjectHydrator->getEntity()\n#4 /vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(143): ORM\\Internal\\Hydration\\ObjectHydrator->hydrateRowData()\n#5 /vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php(270): ORM\\Internal\\Hydration\\ObjectHydrator->hydrateAllData()\n#6 /vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1201): ORM\\Internal\\Hydration\\AbstractHydrator->hydrateAll()\n#7 /vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1142): ORM\\AbstractQuery->executeIgnoreQueryCache()\n#8 /vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(878): ORM\\AbstractQuery->execute()\n#9 /src/Repository/ProductRepository.php(137): ORM\\AbstractQuery->getResult()\n#10 /src/Handler/Query/GetActiveProductQueryHandler.php(18): Repository\\ProductRepository->findActivePlans()\n#11 /vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php(95): Handler\\Query\\GetActiveProductQueryHandler->__invoke()\n#12 /vendor/symfony/messenger/Middleware/SendMessageMiddleware.php(73): Middleware\\HandleMessageMiddleware->handle()\n#13 /vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php(34): Middleware\\SendMessageMiddleware->handle()\n#14 /vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php(68): Middleware\\FailedMessageProcessingMiddleware->handle()\n#15 /vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php(48): Middleware\\DispatchAfterCurrentBusMiddleware->handle()\n#16 /vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php(37): Middleware\\RejectRedeliveredMessageMiddleware->handle()\n#17 /vendor/symfony/messenger/Middleware/TraceableMiddleware.php(43): Middleware\\AddBusNameStampMiddleware->handle()\n#18 /vendor/symfony/messenger/MessageBus.php(77): Messenger\\Middleware\\TraceableMiddleware->handle()\n#19 /vendor/symfony/messenger/HandleTrait.php(43): Messenger\\MessageBus->dispatch()\n#20 /vendor/cqrs/Bus/QueryBus.php(36): CQRS\\Bus\\QueryBus->handleQuery()\n#21 /vendor/cqrs/System/Middleware/HandleQueryMiddleware.php(34): CQRS\\Bus\\QueryBus->handle()\n#22 /vendor/cqrs/System/System.php(94): CQRS\\System\\Middleware\\HandleQueryMiddleware->handle()\n#23 /vendor/cqrs/System/System.php(100): CQRS\\System\\System->{closure}()\n#24 /vendor/cqrs/System/System.php(58): CQRS\\System\\System->handleMessage()\n#25 /src/Controller/ProductController.php(50): CQRS\\System\\System->query()\n#26 /vendor/symfony/http-kernel/HttpKernel.php(152): Controller\\ProductController->getPlans()\n#27 /vendor/symfony/http-kernel/HttpKernel.php(74): HttpKernel->handleRaw()\n#28 /vendor/symfony/http-kernel/Kernel.php(202): HttpKernel->handle()\n#29 /public/index.php(30): Kernel->handle()\n#30 {main}"
        }
    ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant