doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.94k stars 2.52k forks source link

NamingStrategy::referenceColumnName not used in OneToOne relation (Annotations) #6518

Open jennevdmeer opened 7 years ago

jennevdmeer commented 7 years ago

Some obligatory versions to start with quoted from the source files:


I tried implementing a custom naming strategy for increased auto naming flexibility and added CamelCase. This is currently loaded and works with the exception of:

In the case of a OneToOne using annotations (have not checked other methods), referenceColumnName() is not referenced in the call chain all the way back to the JoinColumn::$referencedColumnName class default (id), resulting in the error:

The referenced column name 'id' has to be a primary key column on the target entity class 'AppBundle\Entity\Webshop\Product\Item'

As it should be Id (Workaround's at the end). Now starting with my: class CamelCaseNamingStrategy implements NamingStrategy

....
public function referenceColumnName() {
    return 'Id';
}
....

Is here my manual trace to find out the origin of the lowercase 'id' starting at the point of my error message:

SchemaValidator.php#L208

if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
    $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
           "has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
}

So I dumped the $joinColumn:

"joinColumns" => array:1 [
    0 => array:6 [
        "name" => "CollectionId"
        "unique" => true
        "nullable" => true
        "onDelete" => null
        "columnDefinition" => null
        "referencedColumnName" => "id"
    ]
]

And traced the referencedColumnName to ClassMetaDataInfo::_validateAndCompleteOneToOneMapping()

However the passed in $mapping argument already contains "referencedColumnName" => "id" so any of the referencedColumnName = $this->namingStrategy->referenceColumnName() are ignored as they are all conditioned to only work when joinColumns or referencedColumnName == null.

So continueing the callstack traversal:

ClassMetadataInfo.php

AnnotationDriver.php

Where $joinColumnAnnot is:

Doctrine\ORM\Mapping\JoinColumn {#766
    +name: null
    +referencedColumnName: "id"
    +unique: false
    +nullable: true
    +onDelete: null
    +columnDefinition: null
    +fieldName: null
}

Which brought me to JoinColumn.php


So that is where it stopped for me. Changing that to Id solves it, or just removing the default value seems to work as well. As im using composer (with symfony) I'd rather not modify vendor classes so for now my workaround is to use @ORM\JoinColumn(referencedColumnName="Id").

I don't know if it was something I did, or it indeed is a bug. As I am just a humble php peasant with little doctrine knowledge this goes too deep down the well for me.

Ocramius commented 7 years ago

@jennevdmeer I updated the link references - remember to not link master, but absolute hashes or tags, because master moves ;-)

Ocramius commented 7 years ago

@jennevdmeer you tried really hard to describe what is going on here, and indeed it's a long standing bug about the default value of $referencedColumnName, which should be inferred rather than pre-assigned.

Still, a test case would be much more efficient for starting a fix: see https://github.com/doctrine/doctrine2/tree/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/tests/Doctrine/Tests/ORM/Functional/Ticket for examples

The test would just:

Eventually you can also configure the ORM with a fake naming strategy there (a mock, for example) to check that it is being called.

From that point on, it's all just implementation details about how to fix this properly.