Failing inverse One-to-One relationship using a custom data type #5159

Closed
opened 2026-01-22 14:59:59 +01:00 by admin · 4 comments
Owner

Originally created by @alexb-uk on GitHub (Jun 20, 2016).

Originally assigned to: @lcobucci on GitHub.

Issue

The BasicEntityPersister, possibly loadOneToOneEntity() method, does not handle One-to-One relationships on the inverse side when using a custom data type.

I've stepped through the code and can see that getTypes() method is failing to find a mapping due to the SQL field name being passed in rather than the class field. E.g. from the example below its "t0.shipping_id" but I think should be "shipping". Unfortunately my understanding of the internals of Doctrine is not enough to spot the exact cause / place to fix.

Steps to reproduce

Create two classes with a one-to-one relationship using a custom data type. I used the Ramsey UUID library and Doctrine integration.

Create DQL that filters based on the inverse side and not that hydrated object returned has NULL for the relationship.

Filtering on owning side or using entityManager-find('Shipping', $id) both work correctly.

If I can give any more information or perform any tests please don't hesistate to ask.

Versions

doctrine/common v2.5.3
doctrine/dbal v2.5.4
doctrine/doctrine-module 1.0.0
doctrine/doctrine-orm-module 0.10.0
doctrine/orm v2.5.4
ramsey/uuid-doctrine 1.2.0

YAML mappings

Product:
  type: entity
  table: products
  id:
    id:
      type: uuid_binary
      generator:
        strategy: CUSTOM
      customIdGenerator:
        class: Ramsey\Uuid\Doctrine\UuidGenerator

  fields:
    name:
      type: string

  oneToOne:
      shipping:
        targetEntity: Voicesafe\Test\Shipping
        joinColumn:
          name: shipping_id
          referencedColumnName: id

Shipping:
  type: entity
  table: shipping
  id:
    id:
      type: uuid_binary
      generator:
        strategy: CUSTOM
      customIdGenerator:
        class: Ramsey\Uuid\Doctrine\UuidGenerator

  fields:
      transport:
        type: string

  oneToOne:
      product:
        targetEntity: Voicesafe\Test\Product
        mappedBy: shipping

Working Example - Owning Side

$qb = $entityManager->createQueryBuilder();

$qb->select('p')
    ->from('Product', 'p')
    ->where('p.shipping = ?1')
    ->setParameter(1, $id, 'uuid_binary');

$query  = $qb->getQuery();
$result = $query->getResult();

Debug::dump($result);

SQL Generated

SELECT p0_.id AS id_0, p0_.name AS name_1, p0_.shipping_id AS shipping_id_2 FROM products p0_ WHERE p0_.shipping_id = ')�.GF����\"'
SELECT t0.id AS id_1, t0.transport AS transport_2, t3.id AS id_4, t3.name AS name_5, t3.shipping_id AS shipping_id_6 FROM shipping t0 LEFT JOIN products t3 ON t3.shipping_id = t0.id WHERE t0.id = ')�.GF����\"'

Broken Example - Inverse side

$qb = $entityManager->createQueryBuilder();

$qb->select('s')
    ->from('Shipping', 's')
    ->where('s.id = ?1')
    ->setParameter(1, $id, 'uuid_binary');

$query  = $qb->getQuery();
$result = $query->getResult();

Debug::dump($result);

SQL Generated

SELECT s0_.id AS id_0, s0_.transport AS transport_1 FROM shipping s0_ WHERE s0_.id = ')�.GF����\"'
SELECT t0.id AS id_1, t0.name AS name_2, t0.shipping_id AS shipping_id_3 FROM products t0 WHERE t0.shipping_id = '2901d32e-1247-4615-9f13-baafec221604'

Note: shipping_id is using the string representation not database value.

Thanks a lot for any help / advice or even a suggested bug fix.

Alex

Originally created by @alexb-uk on GitHub (Jun 20, 2016). Originally assigned to: @lcobucci on GitHub. ## Issue The BasicEntityPersister, possibly loadOneToOneEntity() method, does not handle One-to-One relationships on the inverse side when using a custom data type. I've stepped through the code and can see that getTypes() method is failing to find a mapping due to the SQL field name being passed in rather than the class field. E.g. from the example below its "t0.shipping_id" but I think should be "shipping". Unfortunately my understanding of the internals of Doctrine is not enough to spot the exact cause / place to fix. ## Steps to reproduce Create two classes with a one-to-one relationship using a custom data type. I used the Ramsey UUID library and Doctrine integration. Create DQL that filters based on the inverse side and not that hydrated object returned has NULL for the relationship. Filtering on owning side or using entityManager-find('Shipping', $id) both work correctly. If I can give any more information or perform any tests please don't hesistate to ask. ## Versions doctrine/common v2.5.3 doctrine/dbal v2.5.4 doctrine/doctrine-module 1.0.0 doctrine/doctrine-orm-module 0.10.0 doctrine/orm v2.5.4 ramsey/uuid-doctrine 1.2.0 ## YAML mappings ``` Product: type: entity table: products id: id: type: uuid_binary generator: strategy: CUSTOM customIdGenerator: class: Ramsey\Uuid\Doctrine\UuidGenerator fields: name: type: string oneToOne: shipping: targetEntity: Voicesafe\Test\Shipping joinColumn: name: shipping_id referencedColumnName: id Shipping: type: entity table: shipping id: id: type: uuid_binary generator: strategy: CUSTOM customIdGenerator: class: Ramsey\Uuid\Doctrine\UuidGenerator fields: transport: type: string oneToOne: product: targetEntity: Voicesafe\Test\Product mappedBy: shipping ``` ## Working Example - Owning Side ``` $qb = $entityManager->createQueryBuilder(); $qb->select('p') ->from('Product', 'p') ->where('p.shipping = ?1') ->setParameter(1, $id, 'uuid_binary'); $query = $qb->getQuery(); $result = $query->getResult(); Debug::dump($result); ``` ## SQL Generated ``` SELECT p0_.id AS id_0, p0_.name AS name_1, p0_.shipping_id AS shipping_id_2 FROM products p0_ WHERE p0_.shipping_id = ')�.GF����\"' SELECT t0.id AS id_1, t0.transport AS transport_2, t3.id AS id_4, t3.name AS name_5, t3.shipping_id AS shipping_id_6 FROM shipping t0 LEFT JOIN products t3 ON t3.shipping_id = t0.id WHERE t0.id = ')�.GF����\"' ``` ## Broken Example - Inverse side ``` $qb = $entityManager->createQueryBuilder(); $qb->select('s') ->from('Shipping', 's') ->where('s.id = ?1') ->setParameter(1, $id, 'uuid_binary'); $query = $qb->getQuery(); $result = $query->getResult(); Debug::dump($result); ``` ## SQL Generated ``` SELECT s0_.id AS id_0, s0_.transport AS transport_1 FROM shipping s0_ WHERE s0_.id = ')�.GF����\"' SELECT t0.id AS id_1, t0.name AS name_2, t0.shipping_id AS shipping_id_3 FROM products t0 WHERE t0.shipping_id = '2901d32e-1247-4615-9f13-baafec221604' ``` Note: shipping_id is using the string representation not database value. Thanks a lot for any help / advice or even a suggested bug fix. Alex
admin added the Bug label 2026-01-22 14:59:59 +01:00
admin closed this issue 2026-01-22 14:59:59 +01:00
Author
Owner

@liamjay commented on GitHub (Aug 16, 2016):

Can someone please have a look into this bug. I would look into myself but will probably end up breaking stuff!!!

@liamjay commented on GitHub (Aug 16, 2016): Can someone please have a look into this bug. I would look into myself but will probably end up breaking stuff!!!
Author
Owner

@necsord commented on GitHub (Feb 3, 2017):

I came across this bug and while debugging I found following line:

BasicEntityPersister.php#L786

$identifier[$this->getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
    $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);

Keys of $identifier array are being used in BasicEntityPersister::getTypes but since the keys are in the form of table_alias.column_name e.g. t0_.user_id it can't find the appropriate type.

After changing previously mentioned line to:

$identifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
    $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);

Which results in keys being in the form of mapped class field name and in BasicEntityPersister::getTypes it gets mapped correctly using target class associationMappings.

I did not test this heavily but it works for the case with which I had trouble but as I never worked with doctrine source code it would be best if someone took a look at it. I'll try creating pull request for this over the weekend but if someone wants to do it sooner feel free.

@necsord commented on GitHub (Feb 3, 2017): I came across this bug and while debugging I found following line: [BasicEntityPersister.php#L786](https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php#L786) ``` $identifier[$this->getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); ``` Keys of `$identifier` array are being used in `BasicEntityPersister::getTypes` but since the keys are in the form of `table_alias.column_name` e.g. `t0_.user_id` it can't find the appropriate type. After changing previously mentioned line to: ``` $identifier[$targetClass->getFieldForColumn($targetKeyColumn)] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); ``` Which results in keys being in the form of mapped class field name and in `BasicEntityPersister::getTypes` it gets mapped correctly using target class `associationMappings`. I did not test this heavily but it works for the case with which I had trouble but as I never worked with doctrine source code it would be best if someone took a look at it. I'll try creating pull request for this over the weekend but if someone wants to do it sooner feel free.
Author
Owner

@lcobucci commented on GitHub (Apr 30, 2017):

@alexb-uk can you please check if that is fixed on master since #6274 got merged?

@lcobucci commented on GitHub (Apr 30, 2017): @alexb-uk can you please check if that is fixed on `master` since #6274 got merged?
Author
Owner

@Ma27 commented on GitHub (Jan 23, 2019):

I'm afraid this has implications on bidirectional one-to-one relations with multiple JoinColumns: #7579

@Ma27 commented on GitHub (Jan 23, 2019): I'm afraid this has implications on bidirectional one-to-one relations with multiple JoinColumns: #7579
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5159