Doctrine2 Map entities with composite foreign keys

2020-04-16 05:54发布

问题:

I have a model which has many tables, but in this case we only need three.

The point is that the composite primary key of one is also the foreing key (composite too) and Symfony throws this exception:

MappingException: It is not possible to map entity 'Your\SomethingBundle\Entity\Empleado' with a composite primary key as part of the primary key of another entity 'Your\SomethingBundle\Entity\EmpleadoHorario#empleado'.

Here I explain the relationship:

1º Salon, it has a primary key ID

2º Empleado, it has a composite primary key ID, Salon_id and, also in the primary key, a foreing key referencing Salon: Salon_id

3º EmpleadoHorario: it has a composite primary key Fecha, Empleado_id, Salon_id and, also in the primary key, two a foreing keys referencing Salon: Salon_id, and Empleado: Empleado_id, Salon_id

All the relations has also the inverse union. Here is the code:

The Salon Entity:

/**
 * Salon
 *
 * @ORM\Table(name="salon")
 * @ORM\Entity
 */
class Salon
{
    /**
     * @var string
     *
     * @ORM\Column(name="id", type="string", length=50, nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    // More fields...

    /**
     * @var array_collection 
     * 
     * @ORM\OneToMany(targetEntity="Empleado", mappedBy="salon")
     */
    private $empleados;

    /**
     * @var array_collection 
     * 
     * @ORM\OneToMany(targetEntity="EmpleadoHorario", mappedBy="salon")
     */
    private $empleadoHorarios;

    // Getters & Setters...
}

The Empleado Entity:

/**
 * Empleado
 *
 * @ORM\Table(name="empleado")
 * @ORM\Entity
 */
class Empleado
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\JoinColumn(name="salon_id", referencedColumnName="id", nullable=false)
     * @ORM\ManyToOne(targetEntity="Salon", inversedBy="empleados")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $salon;

    // More fields...

    /**
     * @var array_collection 
     * 
     * @ORM\OneToMany(targetEntity="EmpleadoHorario", mappedBy="salon")
     */
    private $empleadoHorarios;

    // Getters & setters...

}

And finally the EmpleadoHorario Entity:

/**
 * EmpleadoHorario
 *
 * @ORM\Table(name="empleado_horario")
 * @ORM\Entity
 */
class EmpleadoHorario
{
    /**
     * @var \DateTime
     *
     * @ORM\Column(name="fecha", type="date", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $fecha;

    /**
     * @var string
     *
     * @ORM\JoinColumn(name="salon_id", referencedColumnName="id", nullable=false)
     * @ORM\ManyToOne(targetEntity="Salon", inversedBy="empleadoHorarios")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $salon;

    /**
     * @var integer
     *
     * @ORM\ManyToOne(targetEntity="Empleado", inversedBy="empleadoHorarios")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="salon_id", referencedColumnName="salon_id", nullable=false),
     *   @ORM\JoinColumn(name="empleado_id", referencedColumnName="id", nullable=false)
     * })
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $empleado;

    // More fields...

    // Getters & Setters...

}

As I said above, the problem seems to be in the EmpleadoHorario.empleado field, which is part of a composite primary key and also composite foreing key.

Other answers across StackOverflow.com suggest the Mapping Inheritance, but I don't even know how it works. I tried twice after reading this but I couldn't solve my problem.

回答1:

This sample code is illustration of my (temporary) solution :

    <?php
namespace X;

use Doctrine\ORM\Mapping as Orm;

/**
 * @Orm\Entity
 * @Orm\Table(name="A")
 */
class A {
    /**
     * @Orm\Id
     * @Orm\Column(name="id", type="integer")
     * @Orm\GeneratedValue(strategy="NONE")
     * 
     * @var integer
     */
    private $id;

    /**
     * @Orm\Id
     * 
     * @var string
     */
    private $otherId;

    /**
     * @Orm\OneToMany(targetEntity="B", fetch="LAZY", mappedBy="a")
     * 
     * @var array
     */
    private $collectionOfB;

    // getter, setter and other props/methods
}
/**
 * @Orm\Entity
 * @Orm\Table(name="B")
 */
class B {
    /**
     * @Orm\Id
     * @Orm\Column(name="code")
     * 
     * @var string
     */
    private $code;

    /**
     * @Orm\Id
     * @Orm\Column(name="a_id", type="integer")
     * 
     * @var integer
     */
    private $a_id;

    /**
     * @Orm\Id
     * @Orm\Column(name="a_other_id")
     *
     * @var integer
     */
    private $a_other_id;

    /**
     * @Orm\ManyToOne(targetEntity="A", fetch="LAZY", inversedBy="collectionOfB")
     * @Orm\JoinColumns({@Orm\JoinColumn(name="a_id", referencedColumnName="id"), @Orm\JoinColumn(name="a_other_id", referencedColumnName="other_id")})
     * 
     * @var A
     */
    private $a;

    /**
     * @Orm\OneToOne(targetEntity="C", fetch="LAZY", mappedBy="b")
     * 
     * @var C
     */
    private $c;

    // bla bla bla
}
/**
 * @Orm\Entity
 * @Orm\Table(name="C")
 */
class C {
    /**
     * @Orm\Id
     * @Orm\Column(name="a_id", type="integer")
     * 
     * @var integer
     */
    private $a_id;

    /**
     * @Orm\Id
     * @Orm\Column(name="a_other_id")
     *
     * @var integer
     */
    private $a_other_id;

    /**
     * @Orm\Id
     * @Orm\Column(name="b_code")
     *
     * @var string
     */
    private $b_code;

    /**
     * 
     * @Orm\OneToOne(targetEntity="B", fetch="LAZY", inversedBy="c")
     * @Orm\JoinColumns({@Orm\JoinColumn(name="a_id", referencedColumnName="a_id"), @Orm\JoinColumn(name="a_other_id", referencedColumnName="a_other_id"), @Orm\JoinColumn(name="b_code", referencedColumnName="b_code")})
     * 
     * @var B
     */
    private $b;

    // bla bla bla
}


回答2:

Another also ugly solution is just to give AUTOINCREMENT unique PK for EmpleadoHorario like id for instance and remove @ORM\Id and @ORM\GeneratedValue(strategy="NONE") notations from EmpleadoHorario. So finally it will look like:

    /**
 * EmpleadoHorario
 *
 * @ORM\Table(name="empleado_horario")
 * @ORM\Entity
 */
class EmpleadoHorario
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="fecha", type="date", nullable=false)
     */
    private $fecha;

    /**
     * @var string
     *
     * @ORM\JoinColumn(name="salon_id", referencedColumnName="id", nullable=false)
     * @ORM\ManyToOne(targetEntity="Salon", inversedBy="empleadoHorarios")
     */
    private $salon;

    /**
     * @var integer
     *
     * @ORM\ManyToOne(targetEntity="Empleado", inversedBy="empleadoHorarios")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="salon_id", referencedColumnName="salon_id", nullable=false),
     *   @ORM\JoinColumn(name="empleado_id", referencedColumnName="id", nullable=false)
     * })
     */
    private $empleado;

    // More fields...

    // Getters & Setters...

}