ManyToOne for composite foreign key with null values #6957

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

Originally created by @danijelvenus on GitHub (Apr 7, 2022).

Bug Report

Q A
BC Break yes
Version 2.11.1

Summary

We need to defined orm mapping for tables with composite keys
Table A
Column A1 not null
Column A2 not null
Column A3 not null

PK for table A is composite and consists of A1 and A2 columns

Table B
Column B1 not null
Column B2 not null
Column B3 null

PK for table B is B1 and B2.

Table B has foreign key to table A (B1 -> A1, B3 -> A2)

We need ManyToOne mapping in entity A

Current behavior

In case when there are null values in fk column B3, but B1 is not null
Exception is thrown when try to read data using Paginator and QueryBuilder

Doctrine\ORM\Exception\MissingIdentifierField::fromFieldAndClass

with message
The identifier 'colum_name' is missing for a query of 'entity_name'

How to reproduce

/**
 *
 * @Entity
 * @Table(name="ORGANIZATIONAL_UNITS")
 */

class OrganizationalUnits implements JsonSerializable
{
    
    /**
     *
     * @Id
     * @Column(type="string", name="ORG_ID", length = 2)
     * @var string
     */
    private $orgId;
    
    
    /**
     *
     * @Id
     * @Column(name = "UNIT_ID", type="integer", precision = 6)
     * @var int
     */
    private $unitId;
    
    /**
     *
     * @Column(type="string", name="UNIT_NAME", length = 160)
     * @var string
     */
    private $unitName;

...

/**
 *
 * @Entity
 * @Table(name="WEB_NEWS")
 *
 */

class WebNews implements JsonSerializable
{
    
    /**
     *
     * @Id
     * @Column(type="string", name="ORG_ID", length = 2)
     * @var string
     */
    private $orgId;
    
    /**
     *
     * @Id
     * @Column(type="string", name="TYPE_ID", length = 2)
     * @var string
     */
    private $typeID;

 /**
     *
     * @Column(name = "UNIT_ID", type="integer", precision = 6, nullable=true)
     * @var int
     */
    private $unitId;
    ...

 /**
     *
     * @ManyToOne(targetEntity="OrganizationalUnits", fetch="EAGER")
     *  @JoinColumns(
     *   @JoinColumn(name = "ORG_ID", referencedColumnName = "ORG_ID"),
     *   @JoinColumn(name = "UNIT_ID", referencedColumnName = "UNIT_ID", nullable=true))
     * @var OrganizationalUnits
     */
    private $organizationalUnits;

...

The exception is thrown in case when there is unitId with null, but orgId is not null in WebNews in DB
with the message
The identifier unitId is missing for a query of '...\OrganizationalUnits'

Expected behavior

Result of paginator and query builder should be a list of rows from database

Is seems to work if we remove marked code below from elseif condition
Doctrine\ORM\UnitOfWork createEntity method

// TODO: Is this even computed right in all cases of composite keys?
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
                        $joinColumnValue = $data[$srcColumn] ?? null;

                        if ($joinColumnValue !== null) {
                            if ($targetClass->containsForeignIdentifier) {
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
                            } else {
                                $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
                            }
                        } elseif (

$targetClass->containsForeignIdentifier

                            && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)
                        ) {
                            // the missing key is part of target's entity primary key
                            $associatedId = [];
                            break;
                        }
                    }
Originally created by @danijelvenus on GitHub (Apr 7, 2022). ### Bug Report | Q | A |------------ | ------ | BC Break | yes | Version | 2.11.1 #### Summary We need to defined orm mapping for tables with composite keys Table A Column A1 not null Column A2 not null Column A3 not null PK for table A is composite and consists of A1 and A2 columns Table B Column B1 not null Column B2 not null Column B3 null PK for table B is B1 and B2. Table B has foreign key to table A (B1 -> A1, B3 -> A2) We need ManyToOne mapping in entity A #### Current behavior In case when there are null values in fk column B3, but B1 is not null Exception is thrown when try to read data using Paginator and QueryBuilder Doctrine\ORM\Exception\MissingIdentifierField::fromFieldAndClass with message The identifier 'colum_name' is missing for a query of 'entity_name' #### How to reproduce ```php /** * * @Entity * @Table(name="ORGANIZATIONAL_UNITS") */ class OrganizationalUnits implements JsonSerializable { /** * * @Id * @Column(type="string", name="ORG_ID", length = 2) * @var string */ private $orgId; /** * * @Id * @Column(name = "UNIT_ID", type="integer", precision = 6) * @var int */ private $unitId; /** * * @Column(type="string", name="UNIT_NAME", length = 160) * @var string */ private $unitName; ... ``` ```php /** * * @Entity * @Table(name="WEB_NEWS") * */ class WebNews implements JsonSerializable { /** * * @Id * @Column(type="string", name="ORG_ID", length = 2) * @var string */ private $orgId; /** * * @Id * @Column(type="string", name="TYPE_ID", length = 2) * @var string */ private $typeID; /** * * @Column(name = "UNIT_ID", type="integer", precision = 6, nullable=true) * @var int */ private $unitId; ... /** * * @ManyToOne(targetEntity="OrganizationalUnits", fetch="EAGER") * @JoinColumns( * @JoinColumn(name = "ORG_ID", referencedColumnName = "ORG_ID"), * @JoinColumn(name = "UNIT_ID", referencedColumnName = "UNIT_ID", nullable=true)) * @var OrganizationalUnits */ private $organizationalUnits; ... ``` The exception is thrown in case when there is unitId with null, but orgId is not null in WebNews in DB with the message The identifier unitId is missing for a query of '...\OrganizationalUnits' #### Expected behavior Result of paginator and query builder should be a list of rows from database Is seems to work if we remove marked code below from elseif condition Doctrine\ORM\UnitOfWork createEntity method ```php // TODO: Is this even computed right in all cases of composite keys? foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { $joinColumnValue = $data[$srcColumn] ?? null; if ($joinColumnValue !== null) { if ($targetClass->containsForeignIdentifier) { $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue; } else { $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; } } elseif ( ``` **$targetClass->containsForeignIdentifier** ```php && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true) ) { // the missing key is part of target's entity primary key $associatedId = []; break; } } ```
admin added the Bug label 2026-01-22 15:42:09 +01:00
Author
Owner

@Basch commented on GitHub (Mar 21, 2024):

A possible solution is to change the testing condition in UnitOfWork.php line 2470:

                        } elseif (
-                           $targetClass->containsForeignIdentifier
+                           $targetClass->getFieldMapping($targetClass->getFieldForColumn($targetColumn))->nullable === false
                            && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)
                        ) {

This way doctrine still try to find relation entity when value is set to null but only if the target entity column is nullable.

@Basch commented on GitHub (Mar 21, 2024): A possible solution is to change the testing condition in UnitOfWork.php line 2470: ```diff } elseif ( - $targetClass->containsForeignIdentifier + $targetClass->getFieldMapping($targetClass->getFieldForColumn($targetColumn))->nullable === false && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true) ) { ``` This way doctrine still try to find relation entity when value is set to null but only if the target entity column is nullable.
Author
Owner

@Justinas-Jurciukonis commented on GitHub (Mar 29, 2024):

This change actually helps with partial foreign keys (when not all fields are set)

@Justinas-Jurciukonis commented on GitHub (Mar 29, 2024): This change actually helps with partial foreign keys (when not all fields are set)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6957