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

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

Originally created by @jennevdmeer on GitHub (Jun 22, 2017).

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

  • COMMON 2.7.0-DEV
  • DBAL 2.5.12
  • ORM 2.5.5-DEV
  • Symfony 3.3.2

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

  • #L2545 public function mapOneToOne(array $mapping) {

AnnotationDriver.php

  • #L365 $metadata->mapOneToOne($mapping);
  • #L359 $mapping['joinColumns'] = $joinColumns;
  • #L304 $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);

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

  • #L36 public $referencedColumnName = 'id';

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.

Originally created by @jennevdmeer on GitHub (Jun 22, 2017). Some obligatory versions to start with quoted from the source files: * COMMON 2.7.0-DEV * DBAL 2.5.12 * ORM 2.5.5-DEV * Symfony 3.3.2 --- 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: <kbd>class CamelCaseNamingStrategy implements NamingStrategy</kbd> .... 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: <kbd>[SchemaValidator.php#L208](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Tools/SchemaValidator.php#L208)</kbd> 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 <kbd>[ClassMetaDataInfo::_validateAndCompleteOneToOneMapping()](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php#L1569)</kbd> 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: <kbd>ClassMetadataInfo.php</kbd> * [#L2545](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php#L2545) `public function mapOneToOne(array $mapping) {` <kbd>AnnotationDriver.php</kbd> * [#L365](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php#L365) `$metadata->mapOneToOne($mapping);` * [#L359](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php#L359) `$mapping['joinColumns'] = $joinColumns;` * [#L304](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php#L304) `$joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);` 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 <kbd>JoinColumn.php</kbd> * [#L36](https://github.com/doctrine/doctrine2/blob/af1ea1ae1d4f0b945eb37c94732ed7e1e44099d8/lib/Doctrine/ORM/Mapping/JoinColumn.php#L36) `public $referencedColumnName = 'id';` --- 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.
admin added the BugMissing Tests labels 2026-01-22 15:12:00 +01:00
Author
Owner

@Ocramius commented on GitHub (Jun 22, 2017):

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

@Ocramius commented on GitHub (Jun 22, 2017): @jennevdmeer I updated the link references - remember to not link `master`, but absolute hashes or tags, because `master` moves ;-)
Author
Owner

@Ocramius commented on GitHub (Jun 22, 2017):

@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 af1ea1ae1d/tests/Doctrine/Tests/ORM/Functional/Ticket for examples

The test would just:

  • map the schema as you'd expect it to work (via annotations on fake entities)
  • create the schema
  • verify the created column names

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.

@Ocramius commented on GitHub (Jun 22, 2017): @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: * map the schema as you'd expect it to work (via annotations on fake entities) * create the schema * verify the created column names 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.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5585