DQL cannot resolve primary key through one-to-one relationship as parameter

Viewed 67

I use a one-to-one relationship as an id for an entity:

Actor entity

$metadata->mapOneToOne([
    'fieldName' => 'user',
    'id' => true,
    'targetEntity' => AbstractUser::class,
    'inversedBy' => 'actor',
    'cascade' => ['all'],
    'joinColumns' => [
        [
            'name' => 'user_id',
            'referencedColumnName' => 'id',
            'nullable' => false,
        ]
    ],
]);

and the other side:

AbstractUser entity

$metadata->mapField([
    'fieldName' => 'id',
    'type' => 'string',
    'length' => 36,
    'id' => true,
    'strategy' => 'none',
    'unique' => true,
]);

$metadata->mapOneToOne([
    'fieldName' => 'actor',
    'targetEntity' => Actor::class,
    'mappedBy' => 'user',
    'cascade' => ['all'],
]);

Then I have a third entity (Subscription) referencing the Actor entity:

$metadata->mapManyToOne([
    'fieldName' => 'subscribingActor',
    'targetEntity' => Actor::class,
    'joinColumns' => [
        [
            'name' => 'subscribing_actor_id',
            'referencedColumnName' => 'user_id',
            'nullable' => false,
        ],
    ],
]);

The query I try to run looks like this:

function findByActors(Actor $subscribingActor, Actor $subscribedActor): ?Subscription
{
    $qb = $this->entityRepository->createQueryBuilder('s');
    $qb
        ->where('s.subscribingActor = :subscribingActor')
        ->setParameter('subscribingActor', $subscribingActor);

    return $qb->getQuery()->getOneOrNullResult();
}

This results in the following exception:

Object of InternalUser (extends AbstractUser) could not be converted to string

If I implement AbstractUser::__toString() returning the id of the user everything works fine.

The strange thing: if I load the entity freshly from the database, it works. If I create it, persist it and use that every entity object I get the above "to string" error.

My question is now, why is doctrine not able to detect the id value through the mapOneToOne -> joinColumns[0]['referencedColumnName'] information but instead does try to call __toString() on the related object although it's clear from the mapping where it could find the PK value of the related entity but only if the entity is not initially loaded from the database?

2 Answers

Somehow doctrine is just not able to determine the PK of the Actor entity through the mapping. Providing the original entity User helps doctrine resolving everything:

function findByActors(Actor $subscribingActor, Actor $subscribedActor): ?Subscription
{
    $qb = $this->entityRepository->createQueryBuilder('s');
    $qb
        ->where('s.subscribingActor = :subscribingActor')
        ->setParameter('subscribingActor', $subscribingActor->getUser());
        // or $subscribingActor->getUser()->getId() to be even more explicit

    return $qb->getQuery()->getOneOrNullResult();
}

You can test this :

    function findByActors(Actor $subscribingActor): ?Subscription
    {
        $qb = $this->entityRepository->createQueryBuilder('s');
        $qb->leftJoin('s.subscribingActor', 'subscribingActor')
            ->where('subscribingActor.id = :subscribingActorId')
            ->setParameter('subscribingActorId', $subscribingActor->getId());

        return $qb->getQuery()->getOneOrNullResult();
    }

Hope this help

Related