[DDC-2421] Many-To-Many relation creation failed when using non PK entity field Created: 29/Apr/13  Updated: 10/Sep/13  Resolved: 01/May/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers, Tools
Affects Version/s: Git Master, 2.3.3
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Bruno CHALOPIN Assignee: Benjamin Eberlei
Resolution: Invalid Votes: 0
Labels: schematool
Environment:

Ubuntu linux 12.04, php 5.4.9



 Description   

Given these entities :

/**
 * Class Domain
 *
 * @ORM\Entity
 * @ORM\Table(name="profils_domains")
 */
class Domain
{
    /**
     * @var string
     *
     * @ORM\Id
     * @ORM\Column(type="string", length=22, nullable=false)
     */
    protected $name = '';
}
/**
 * Class Web
 *
 * @ORM\Entity
 * @ORM\Table(name="profils_webs",
 *          uniqueConstraints={@ORM\UniqueConstraint(name="web_unique",columns={"name", "domain"})}
 * )
 */
class Web
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer", nullable=false)
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=22, nullable=false)
     */
    protected $name = '';

    /**
     * @var Domain
     *
     * @ORM\ManyToOne(targetEntity="Domain", fetch="LAZY")
     * @ORM\JoinColumn(name="domain", referencedColumnName="name", nullable=false, onDelete="CASCADE")
     */
    protected $domain;
}
/**
 * Class WebsGroup
 *
 * @ORM\Entity
 * @ORM\Table(name="profils_websgroups",
 *          uniqueConstraints={@ORM\UniqueConstraint(name="websgroup_unique",columns={"name", "domain"})}
 * )
 */
class WebsGroup
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer", nullable=false)
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=22, nullable=false)
     */
    protected $name = '';

    /**
     * @var Domain
     *
     * @ORM\ManyToOne(targetEntity="Domain", fetch="LAZY")
     * @ORM\JoinColumn(name="domain", referencedColumnName="name", nullable=false, onDelete="CASCADE")
     */
    protected $domain;

    /**
     * @var ArrayCollection
     *
     * @ORM\ManyToMany(targetEntity="Web", indexBy="id", fetch="EXTRA_LAZY")
     * @ORM\JoinTable(name="profils_websgroups_webs",
     *      joinColumns={
     * @ORM\JoinColumn(name="websgroup_id", referencedColumnName="id", onDelete="CASCADE"),
     * @ORM\JoinColumn(name="domain", referencedColumnName="domain", onDelete="CASCADE")
     *          },
     *      inverseJoinColumns={
     * @ORM\JoinColumn(name="web_id", referencedColumnName="id", onDelete="CASCADE"),
     * @ORM\JoinColumn(name="domain", referencedColumnName="domain", onDelete="CASCADE")
     *          }
     *      )
     */
    protected $webs;
}

I've got a domain, some web sites per domain and websgroups which group web sites. I want to be sure in my database that a web group from a domain D can contain only web sites from the very same domain but when calling the console tool for creating my schema it raise :

[Doctrine\ORM\ORMException]                                                                                                                                      
  Column name `domain` referenced for relation from Entity\WebsGroup towards Entity\Web does not exist. 

It's because domain is already an association to an entity which and is not part of the primary key.

I've quick fixed getDefiningClass from Doctrine\ORM\Tools\SchemaTool to make it work but i really don't know if it's the proper way :

    private function getDefiningClass($class, $referencedColumnName)
    {
        $referencedFieldName = $class->getFieldName($referencedColumnName);

        if ($class->hasField($referencedFieldName)) {
            return array($class, $referencedFieldName);
        } else if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) {
            // it seems to be an entity as foreign key
            foreach ($class->getIdentifierFieldNames() as $fieldName) {
                if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) {
                    return $this->getDefiningClass(
                        $this->em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']),
                        $class->getSingleAssociationReferencedJoinColumnName($fieldName)
                    );
                }
            }
        } else if (in_array($referencedColumnName, $class->getAssociationNames())) {
            return $this->getDefiningClass(
                $this->em->getClassMetadata($class->associationMappings[$referencedColumnName]['targetEntity']),
                $class->getSingleAssociationReferencedJoinColumnName($referencedColumnName)
            );
        }

        return null;
    }


 Comments   
Comment by Fabio B. Silva [ 29/Apr/13 ]

Bruno CHALOPIN Except for some CS this fix seems good.

If you have time you can send as pull request

Comment by Bruno CHALOPIN [ 30/Apr/13 ]

I've start making a PR and a test case but it is linked to http://www.doctrine-project.org/jira/browse/DDC-2413
I'm looking in making a proper fix to DDC-2413 first.

Comment by Benjamin Eberlei [ 01/May/13 ]

You cannot use a reference column that is not a primary key. Doctrine does not support this.

Comment by Bavo Janss [ 10/Sep/13 ]

I stumbled on this issue when experiencing a similar bug.
There is something seriously wrong with the referencedColumn in a ManyToMany relationship.
When you use a PK as 'id' and set your referenced column to 'id' everything is fine.
But when you use 'productId' as PK and reference it properly creation fails because "column 'id' doens't exists in table", which is very strange. After introducing a bogus column 'id' to the entity, but still referencing 'productId' a linking table is created but it has FK's to 'id' not to productId. When changing the referenced column to 'bogus' the creation also fails because: "column 'bogus' doesn't exists in table".

To me doctrine checks the the referencedColumn for existence but after that just creates everything assuming the default PK 'id'.

Generated at Sun Sep 21 14:17:16 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.