Search code examples
symfonydoctrine-ormsingle-table-inheritance

Symfony Doctrine - InheritanceType SINGLE_TABLE is not changing target Entity when Dicscriminator changes but not Id


I'm trying to handle a legacy database with doctrine. There is many fields in different tables that are codes which refer to a unique code table CHOIXCOD. This table relies on two keys. The key (CC_CODE) that corresponds to the value in the linked fields, and a type code (CC_TYPE) identifying which field the first key is targeting.

So i used Doctrine Inheritance on an abstract entity targeting this code table, and created as many children entities as there is difference types. What i understand is that Doctrine will make snippets of CHOIXCOD on the CC_TYPE field, and in each snippet, CC_CODE will be the key column.

But, when all $ccCode are differents all works fine, but, in one table having multiple references to CHOIXCOD, if two fields referencing CC_CODE have the same value, Doctrine will give them the same CC_TYPE value, resulting in targeting the wrong entity, crash, 500 error, project being late, me being fired.

So here is the mother entity.

<?php

namespace App\Entity;


use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="CHOIXCOD")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="CC_TYPE", type="string", length=3)
 * @ORM\DiscriminatorMap({"PHV" = "Phasevocale",
 *                        "TMS" = "Typemessage",
 *                        "PRI" = "Priorite",
 *                        "ZEL" = "Etatlivraison",
 *                        "ZEM" = "Etatmessage",
 *                        "ZOR" = "Originems"})
 */

abstract class Choixcod
{

    /**
     * @var string|null
     *
     * @ORM\Column(name="CC_CODE", type="string", length=3, nullable=true)
     * @ORM\Id
     */
    protected $ccCode;

    /**
     * @var string|null
     *
     * @ORM\Column(name="CC_LIBELLE", type="string", length=105, nullable=true)
     */
    protected $ccLibelle;

    /**
     * @var string|null
     *
     * @ORM\Column(name="CC_ABREGE", type="string", length=17, nullable=true)
     */
    protected $ccAbrege;

    /**
     * @var string|null
     *
     * @ORM\Column(name="CC_LIBRE", type="string", length=70, nullable=true)
     */
    protected $ccLibre;

And all the children are like this

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
*/
class Etatmessage extends Choixcod{}

They are then used in other entities following a ManyToOne relation

    /**
     *
     * @ORM\ManyToOne(targetEntity="Etatmessage")
     * @ORM\JoinColumn(name="ZMS_ETATMESSAGE", referencedColumnName="CC_CODE")
     * @ToNull(primaryKey="ccCode")
     */
    private $zmsEtatmessage;

The @ToNull annotation is one we wrote to handle the empty or full of spaces foreign keys). I tried to removed them, that doesn't change anything.

Any idea welcome.


Solution

  • Ok, so after struggling with that problem for a while, I understood that Doctrine stores a two dimensions array in order to request joins (1-entity joined, 2-id of entity instance). I suppose the aim is to limit the number of request done for a yet known instance.

    In our case, inheritance, the first dimension of the array is named with the extended entity so as I had the same ids for the same first dimension name that was messed up from the beginning. Setting the discriminator as @Id could have solved this problem but Doctrine does not automatically set the discriminator value in object, throwing an id missing exception.

    So the solution I kept is to put an annotation on foreign key that specifies discriminator value this foreign key corresponds to. When encountered in doctrine postLoad event, I request the database on foreign key value (the primary key corresponding being CC_CODE) and discriminator (the value of CC_TYPE) in annotation, and put it into an another field.