Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with association on composite key entities with Doctrine2?

Say I have an Offer, which can have 1-n Range. Immediately you think, "put a offer_id inside Range".

But my Offer has a composite primary key (composed of two fields). There is no AUTOINCREMENT id column.

The Doctrine2 documentation doesn't say much about that particular case, here is my entities:

<?php
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Offer
{
    /**
     * @var Site $site
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Site")
     * @ORM\JoinColumn(name="site_id", referencedColumnName="id")
     */
        private $site;

    /**
     * @var string $pouet
     * @ORM\Id
     * @ORM\Column(name="pouet", type="string", length=255)
     */
    private $pouet;
}

<?php
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="RangeItem")
 * @ORM\Entity
 */
class Range
{
    /**
     * @todo This is test code only do not push me :-)
     * @var ArrayCollection
     * @ORM\ManyToOne(targetEntity="Offer")
     */
    private $offers;
}

I obtaind this error:

[Doctrine\ORM\ORMException]
Column name id referenced for relation from Pouet\MyBundle\Entity\Range towards Pouet\MyBundle\Entity\Offer does not exist.

That make sense, but how can I deal with this issue? Is a Table with composite primary key forbidden to have associations on it?

like image 387
Damien Avatar asked Aug 03 '11 12:08

Damien


2 Answers

I believe the solution is to mirror the primary key (PK) for the foreign key (FK). I.E. for each column that makes up the PK (site, pouet) you need to have the same columns on the related entity.

You can do this by using the JoinColumns annotation (or the equivalent in YAML/XML) with a JoinColumn for each part of the composite FK:

/**
 * @ORM\Table(name="RangeItem")
 * @ORM\Entity
 */
class Range
{
    /**
     * @todo This is test code only do not push me :-)
     * @var ArrayCollection
     * @ORM\ManyToOne(targetEntity="Offer")
     * @ORM\JoinColumns(
     *     @ORM\JoinColumn(name="site_id", referencedColumnName="site_id"),
     *     @ORM\JoinColumn(name="pouet", referencedColumnName="pouet")
     * )
     */
    private $offers;
}

I hope this might help somebody who is still struggling with this issue.

like image 145
Kenny Body Avatar answered Nov 01 '22 13:11

Kenny Body


You should be able to use a @JoinColumn annotation in the Range class to specify which Id to use:

/**
 * @ORM\ManyToOne(targetEntity="Offer")
 * @ORM\JoinColumn(name="offer_pouet", referencedColumnName="pouet")
 */
private $offers;

Because the defaults for @JoinColumn, if you do not specify them, would be offer_id and id, respectively, you need to manually specify (I'm making a bit of an assumption here that pouet is a unique value for your Offer class).

EDIT: based on your comment, I found a tutorial on the Doctrine Project site for Composite Primary Key. The entity relationship has mappedBy for one key and indexBy for the other. Hope that helps.

like image 1
Derek Stobbe Avatar answered Nov 01 '22 13:11

Derek Stobbe