Duplicate definition of column … on inherited embeddable #5916

Open
opened 2026-01-22 15:22:00 +01:00 by admin · 6 comments
Owner

Originally created by @havvg on GitHub (Mar 9, 2018).

Hi there, I got two value objects, Timeframe and BoundedTimeframe, which extends Timeframe.

When I try to setup the mapping file for BoundedTimeframe I get the following exception (ORM v2.5.12). I tried removing the duplicate definitions from the mapping file, no success. How is it possible to use embeddable which are part of inheritance?

  [Doctrine\ORM\Mapping\MappingException]
  Duplicate definition of column 'from' on entity 'BoundedTimeframe' in a field or discriminator column mapping.


Exception trace:
 () at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php:565
 Doctrine\ORM\Mapping\MappingException::duplicateColumnName() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:1393
 Doctrine\ORM\Mapping\ClassMetadataInfo->_validateAndCompleteFieldMapping() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:2314
 Doctrine\ORM\Mapping\ClassMetadataInfo->mapField() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php:328
 Doctrine\ORM\Mapping\Driver\YamlDriver->loadMetadataForClass() at …vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php:102
 Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain->loadMetadataForClass() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:151
Timeframe:
    type: embeddable

    fields:
        from:
            type: datetime_immutable
            nullable: true

        to:
            type: datetime_immutable
            nullable: true

BoundedTimeframe:
    type: embeddable

    fields:
        from:
            type: datetime_immutable
            nullable: false

        to:
            type: datetime_immutable
            nullable: false
<?php

declare(strict_types=1);

namespace Thengine\Domain\ValueObject;

use Havvg\Tools\DateTime;
use Thengine\Doctrine\ORM\NullableEmbeddableInterface;

class Timeframe implements NullableEmbeddableInterface
{
    private $from;
    private $to;

    public function __construct(\DateTimeInterface $from = null, \DateTimeInterface $to = null)
    {
        $this->from = $from ? DateTime::ensureImmutable($from) : null;
        $this->to = $to ? DateTime::ensureImmutable($to) : null;
    }

    public function getFrom(): ?\DateTimeImmutable
    {
        return $this->from;
    }

    public function getTo(): ?\DateTimeImmutable
    {
        return $this->to;
    }

    // …
}
<?php

declare(strict_types=1);

namespace Thengine\Domain\ValueObject;

use Thengine\Domain\Assert\Assert;

class BoundedTimeframe extends Timeframe
{
    public function __construct(\DateTimeInterface $from, \DateTimeInterface $to)
    {
        parent::__construct($from, $to);
    }

    public function getFrom(): \DateTimeImmutable
    {
        return parent::getFrom();
    }

    public function getTo(): \DateTimeImmutable
    {
        return parent::getTo();
    }

    // …
}
Originally created by @havvg on GitHub (Mar 9, 2018). Hi there, I got two value objects, `Timeframe` and `BoundedTimeframe`, which extends `Timeframe`. When I try to setup the mapping file for `BoundedTimeframe` I get the following exception (ORM v2.5.12). I tried removing the duplicate definitions from the mapping file, no success. How is it possible to use embeddable which are part of inheritance? ``` [Doctrine\ORM\Mapping\MappingException] Duplicate definition of column 'from' on entity 'BoundedTimeframe' in a field or discriminator column mapping. Exception trace: () at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php:565 Doctrine\ORM\Mapping\MappingException::duplicateColumnName() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:1393 Doctrine\ORM\Mapping\ClassMetadataInfo->_validateAndCompleteFieldMapping() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:2314 Doctrine\ORM\Mapping\ClassMetadataInfo->mapField() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php:328 Doctrine\ORM\Mapping\Driver\YamlDriver->loadMetadataForClass() at …vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php:102 Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain->loadMetadataForClass() at …vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:151 ``` ```yaml Timeframe: type: embeddable fields: from: type: datetime_immutable nullable: true to: type: datetime_immutable nullable: true BoundedTimeframe: type: embeddable fields: from: type: datetime_immutable nullable: false to: type: datetime_immutable nullable: false ``` ```php <?php declare(strict_types=1); namespace Thengine\Domain\ValueObject; use Havvg\Tools\DateTime; use Thengine\Doctrine\ORM\NullableEmbeddableInterface; class Timeframe implements NullableEmbeddableInterface { private $from; private $to; public function __construct(\DateTimeInterface $from = null, \DateTimeInterface $to = null) { $this->from = $from ? DateTime::ensureImmutable($from) : null; $this->to = $to ? DateTime::ensureImmutable($to) : null; } public function getFrom(): ?\DateTimeImmutable { return $this->from; } public function getTo(): ?\DateTimeImmutable { return $this->to; } // … } ``` ```php <?php declare(strict_types=1); namespace Thengine\Domain\ValueObject; use Thengine\Domain\Assert\Assert; class BoundedTimeframe extends Timeframe { public function __construct(\DateTimeInterface $from, \DateTimeInterface $to) { parent::__construct($from, $to); } public function getFrom(): \DateTimeImmutable { return parent::getFrom(); } public function getTo(): \DateTimeImmutable { return parent::getTo(); } // … } ```
Author
Owner

@Ocramius commented on GitHub (Mar 9, 2018):

Please do check ORM 2.6.x first.

@Ocramius commented on GitHub (Mar 9, 2018): Please do check ORM 2.6.x first.
Author
Owner

@havvg commented on GitHub (Mar 9, 2018):

The issue exists on 2.6, too.

Changelogs summary:

 - doctrine/collections updated from v1.3.0 to v1.5.0
   See changes: https://github.com/doctrine/collections/compare/v1.3.0...v1.5.0
   Release notes: https://github.com/doctrine/collections/releases/tag/v1.5.0

 - doctrine/annotations updated from v1.2.7 to v1.6.0
   See changes: https://github.com/doctrine/annotations/compare/v1.2.7...v1.6.0
   Release notes: https://github.com/doctrine/annotations/releases/tag/v1.6.0

 - doctrine/instantiator updated from 1.0.5 to 1.1.0
   See changes: https://github.com/doctrine/instantiator/compare/1.0.5...1.1.0
   Release notes: https://github.com/doctrine/instantiator/releases/tag/1.1.0

 - doctrine/orm updated from v2.5.12 to v2.6.1
   See changes: https://github.com/doctrine/doctrine2/compare/v2.5.12...v2.6.1
   Release notes: https://github.com/doctrine/doctrine2/releases/tag/v2.6.1
@havvg commented on GitHub (Mar 9, 2018): The issue exists on 2.6, too. ``` Changelogs summary: - doctrine/collections updated from v1.3.0 to v1.5.0 See changes: https://github.com/doctrine/collections/compare/v1.3.0...v1.5.0 Release notes: https://github.com/doctrine/collections/releases/tag/v1.5.0 - doctrine/annotations updated from v1.2.7 to v1.6.0 See changes: https://github.com/doctrine/annotations/compare/v1.2.7...v1.6.0 Release notes: https://github.com/doctrine/annotations/releases/tag/v1.6.0 - doctrine/instantiator updated from 1.0.5 to 1.1.0 See changes: https://github.com/doctrine/instantiator/compare/1.0.5...1.1.0 Release notes: https://github.com/doctrine/instantiator/releases/tag/1.1.0 - doctrine/orm updated from v2.5.12 to v2.6.1 See changes: https://github.com/doctrine/doctrine2/compare/v2.5.12...v2.6.1 Release notes: https://github.com/doctrine/doctrine2/releases/tag/v2.6.1 ```
Author
Owner

@Ocramius commented on GitHub (Mar 9, 2018):

Thanks for checking 👍

On 9 Mar 2018 19:05, "Toni Uebernickel" notifications@github.com wrote:

The issue exists on 2.6, too.

Changelogs summary:


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/doctrine/doctrine2/issues/7125#issuecomment-371895491,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAJakDwEfvEvtnuWb9858pShjUk2DL5pks5tcsRVgaJpZM4SkkNH
.

@Ocramius commented on GitHub (Mar 9, 2018): Thanks for checking 👍 On 9 Mar 2018 19:05, "Toni Uebernickel" <notifications@github.com> wrote: > The issue exists on 2.6, too. > > Changelogs summary: > > - doctrine/collections updated from v1.3.0 to v1.5.0 > See changes: https://github.com/doctrine/collections/compare/v1.3.0...v1.5.0 > Release notes: https://github.com/doctrine/collections/releases/tag/v1.5.0 > > - doctrine/annotations updated from v1.2.7 to v1.6.0 > See changes: https://github.com/doctrine/annotations/compare/v1.2.7...v1.6.0 > Release notes: https://github.com/doctrine/annotations/releases/tag/v1.6.0 > > - doctrine/instantiator updated from 1.0.5 to 1.1.0 > See changes: https://github.com/doctrine/instantiator/compare/1.0.5...1.1.0 > Release notes: https://github.com/doctrine/instantiator/releases/tag/1.1.0 > > - doctrine/orm updated from v2.5.12 to v2.6.1 > See changes: https://github.com/doctrine/doctrine2/compare/v2.5.12...v2.6.1 > Release notes: https://github.com/doctrine/doctrine2/releases/tag/v2.6.1 > > — > You are receiving this because you commented. > Reply to this email directly, view it on GitHub > <https://github.com/doctrine/doctrine2/issues/7125#issuecomment-371895491>, > or mute the thread > <https://github.com/notifications/unsubscribe-auth/AAJakDwEfvEvtnuWb9858pShjUk2DL5pks5tcsRVgaJpZM4SkkNH> > . >
Author
Owner

@fabschurt commented on GitHub (Nov 12, 2018):

@havvg I had the EXACT same problem. I know I’m a bit late, but here is how I solved it:

First, you’ll want to use attributeOverride instead of fields in the mapping of the inheriting class:

Timeframe:
    type: embeddable

    fields:
        from:
            type: datetime_immutable
            nullable: true

        to:
            type: datetime_immutable
            nullable: true

BoundedTimeframe:
    type: embeddable

    attributeOverride:
        from:
            type: datetime_immutable
            nullable: false

        to:
            type: datetime_immutable
            nullable: false

Note that you would still have to do that even if the field definitions were exactly the same (here, the nullable attribute has a different value), otherwise you would have problems with the DQL query builder that would use an unmapped base alias for these fields when you would select all fields via $repository->createQueryBuilder('some_alias') for example.

Second, you’ll want to set the visibility of the inherited properties to protected instead of private in the base class:

<?php

// […]

class Timeframe implements NullableEmbeddableInterface
{
    protected $from;
    protected $to;

    // […]
}

I know this is not ideal, but we’re dealing with inheritance here, so nothing will ever be ideal anyway…

It should work 🙂

@Ocramius, do you think this is worth a PR adding some documentation about this particular edge case?

@fabschurt commented on GitHub (Nov 12, 2018): @havvg I had the EXACT same problem. I know I’m a bit late, but here is how I solved it: First, you’ll want to use `attributeOverride` instead of `fields` in the mapping of the inheriting class: ```yaml Timeframe: type: embeddable fields: from: type: datetime_immutable nullable: true to: type: datetime_immutable nullable: true BoundedTimeframe: type: embeddable attributeOverride: from: type: datetime_immutable nullable: false to: type: datetime_immutable nullable: false ``` Note that you would still have to do that even if the field definitions were exactly the same (here, the `nullable` attribute has a different value), otherwise you would have problems with the DQL query builder that would use an unmapped base alias for these fields when you would `select` all fields via `$repository->createQueryBuilder('some_alias')` for example. Second, you’ll want to set the visibility of the inherited properties to `protected` instead of `private` in the base class: ```php <?php // […] class Timeframe implements NullableEmbeddableInterface { protected $from; protected $to; // […] } ``` I know this is not ideal, but we’re dealing with inheritance here, so nothing will ever be ideal anyway… It should work :slightly_smiling_face: @Ocramius, do you think this is worth a PR adding some documentation about this particular edge case?
Author
Owner

@Ocramius commented on GitHub (Nov 12, 2018):

@havvg seems like hacks all the way down there. Would probably be better to have embeddable replace metadata for fields, instead of inheriting parent fields and having the re-declaration rules of entities.

@Ocramius commented on GitHub (Nov 12, 2018): @havvg seems like hacks all the way down there. Would probably be better to have embeddable replace metadata for fields, instead of inheriting parent fields and having the re-declaration rules of entities.
Author
Owner

@fabschurt commented on GitHub (Nov 12, 2018):

@Ocramius Mmh yeah I guess. I can try to make a PR for this 🙂

@fabschurt commented on GitHub (Nov 12, 2018): @Ocramius Mmh yeah I guess. I can try to make a PR for this :slightly_smiling_face:
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5916