Versioned Embeddable will not propagate version to Entity #5206

Open
opened 2026-01-22 15:01:32 +01:00 by admin · 1 comment
Owner

Originally created by @tPl0ch on GitHub (Jul 30, 2016).

Hi guys,

we are currently doing a refactoring of our code base and we are building a service that expects multiple clients to call the API, hence we wanted to explicitly model the concurrency control in our domain. One of our Repository implementations uses the Doctrine ORM for data access.

Since the version is part of an Entity's identity, we have created a CountryId value object that includes the current version. See the XML mapping below:

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                   https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">

    <embeddable name="Flixbus\LocationContext\Location\Domain\Model\Country\CountryId">
        <id name="uuid" column="id" type="guid" column-definition="CHAR(36) NOT NULL">
            <generator strategy="NONE" />
        </id>

        <field name="legacyId" column="legacy_id" type="integer" nullable="true" />
        <field name="version" column="version" version="true" nullable="false" type="integer" />
    </embeddable>

</doctrine-mapping>

We then use this as an Embeddable inside the Country entity:

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                   https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">

    <entity
            name="Flixbus\LocationContext\Location\Domain\Model\Country\Country"
            table="countries">
        <embedded
                name="id"
                class="Flixbus\LocationContext\Location\Domain\Model\Country\CountryId"
                use-column-prefix="false" />
        <!-- SNIP -->
    </entity>

</doctrine-mapping>

In our Repository implementation we then use the Version from the CountryId to call the EntityManager with LockMode::OPTIMISTIC. As a side note, we are injecting an instance of EntityManagerInterface in our repository which actually has no knowledge of the locking parameters. IMO there should be an LockableEntityManagerInterface that defines a findWithLock($className, $id, $lockMode, $version):

<?php

/**
 * Class ORMRepository
 */
class ORMRepository implements ReadRepositoryInterface, WriteRepositoryInterface
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * ORMRepository constructor.
     * @param EntityManagerInterface $entityManagerInterface
     */
    public function __construct(EntityManagerInterface $entityManagerInterface)
    {
        $this->entityManager = $entityManagerInterface;
    }

    /**
     * @param CountryId $id
     *
     * @return Country|null
     */
    public function find(CountryId $id)
    {
        return $this->entityManager->find(Country::class, (string) $id, LockMode::OPTIMISTIC, $id->getVersion());
    }
}

My expectation was that the version definition from the embeddable CountryId would propagate into the Country entity, but apparently I was wrong:

1) Flixbus\LocationContext\Location\Infrastructure\Domain\Model\Country\Doctrine\ORMRepositoryTest::it_can_store_a_new_entity
Doctrine\ORM\OptimisticLockException: Cannot obtain optimistic lock on unversioned entity Flixbus\LocationContext\Location\Domain\Model\Country\Country

I would be up for adding this kind of propagation to the Metadata drivers since I don't want to maintain (another) fork of a library in our dependecies. The question is do you think this feature has a chance of being merged into the upstream? If so, I'd happily provide a patch with test cases.

Regards
Thomas

Originally created by @tPl0ch on GitHub (Jul 30, 2016). Hi guys, we are currently doing a refactoring of our code base and we are building a service that expects multiple clients to call the API, hence we wanted to explicitly model the concurrency control in our domain. One of our Repository implementations uses the Doctrine ORM for data access. Since the version is part of an Entity's identity, we have created a `CountryId` value object that includes the current version. See the XML mapping below: ``` xml <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> <embeddable name="Flixbus\LocationContext\Location\Domain\Model\Country\CountryId"> <id name="uuid" column="id" type="guid" column-definition="CHAR(36) NOT NULL"> <generator strategy="NONE" /> </id> <field name="legacyId" column="legacy_id" type="integer" nullable="true" /> <field name="version" column="version" version="true" nullable="false" type="integer" /> </embeddable> </doctrine-mapping> ``` We then use this as an `Embeddable` inside the `Country` entity: ``` xml <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> <entity name="Flixbus\LocationContext\Location\Domain\Model\Country\Country" table="countries"> <embedded name="id" class="Flixbus\LocationContext\Location\Domain\Model\Country\CountryId" use-column-prefix="false" /> <!-- SNIP --> </entity> </doctrine-mapping> ``` In our Repository implementation we then use the Version from the `CountryId` to call the `EntityManager` with `LockMode::OPTIMISTIC`. As a side note, we are injecting an instance of `EntityManagerInterface` in our repository which actually has no knowledge of the locking parameters. IMO there should be an `LockableEntityManagerInterface` that defines a `findWithLock($className, $id, $lockMode, $version)`: ``` php <?php /** * Class ORMRepository */ class ORMRepository implements ReadRepositoryInterface, WriteRepositoryInterface { /** * @var EntityManagerInterface */ private $entityManager; /** * ORMRepository constructor. * @param EntityManagerInterface $entityManagerInterface */ public function __construct(EntityManagerInterface $entityManagerInterface) { $this->entityManager = $entityManagerInterface; } /** * @param CountryId $id * * @return Country|null */ public function find(CountryId $id) { return $this->entityManager->find(Country::class, (string) $id, LockMode::OPTIMISTIC, $id->getVersion()); } } ``` My expectation was that the version definition from the embeddable `CountryId` would propagate into the `Country` entity, but apparently I was wrong: ``` 1) Flixbus\LocationContext\Location\Infrastructure\Domain\Model\Country\Doctrine\ORMRepositoryTest::it_can_store_a_new_entity Doctrine\ORM\OptimisticLockException: Cannot obtain optimistic lock on unversioned entity Flixbus\LocationContext\Location\Domain\Model\Country\Country ``` I would be up for adding this kind of propagation to the Metadata drivers since I don't want to maintain (another) fork of a library in our dependecies. The question is do you think this feature has a chance of being merged into the upstream? If so, I'd happily provide a patch with test cases. Regards Thomas
Author
Owner

@DHager commented on GitHub (Aug 31, 2016):

From my experience with #1456, expect a long wait.

@DHager commented on GitHub (Aug 31, 2016): From my experience with #1456, expect a long wait.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5206