Associations Being loaded with the wrong type and wrong Id #5282

Closed
opened 2026-01-22 15:03:23 +01:00 by admin · 5 comments
Owner

Originally created by @warslett on GitHub (Oct 3, 2016).

I have an entity that has two ManyToOne associations to two different classes of entity. When the entity is loaded from the repo, both associations are loaded with the same class (so one association has the correct class and the other has the wrong class). The other strange behaviour is that the IDs have been switched around so the id for the first association is loaded for the second association and the id of the second association is loaded for the first association.

These issues only seem to effect retrieving the entity, creating the entity seems to have no problems and the data this is mapped to was created using the same entities with the same association mappings. The data that gets set in the database when these entities are persisted is all correct, the correct Ids are being set on the correct columns. The only thing I think I'm doing that is unusual in any way is I am using a legacy database schema so the schema is not generated by doctrine and there are some columns on the table that are not mapped with doctrine.

My class and mappings without the methods:

/**
 * @ORM\Entity(repositoryClass="AcademyTaskRepository")
 * @ORM\Table(name="academies__task_questions")
 * @ORM\HasLifecycleCallbacks
 */
class AcademyTask implements Owned, TimeStamped
{
    use TimestampTrait, OwnedTrait, ArchiveTrait;

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @var integer
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Academy")
     * @ORM\JoinColumn(name="academy_id", referencedColumnName="id")
     * @var Academy
     */
    private $academy;

    /**
     * @ORM\ManyToOne(targetEntity="CompetencyFrameworkBundle\Entity\Task")
     * @ORM\JoinColumn(name="task_question_id", referencedColumnName="id")
     * @var Task
     */
    private $task;

    /**
     * @ORM\Column(type="integer", name="time_allowed_override")
     * @var integer
     */
    private $timeAllowedOverride = 0;
}

Database:

CREATE TABLE `academies__task_questions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
  `academy_id` int(11) NOT NULL,
  `task_question_id` int(11) NOT NULL,
  `type` varchar(10) NOT NULL DEFAULT 'audition',
  `task_question_override` varchar(255) NOT NULL,
  `description_override` text NOT NULL,
  `time_allowed_override` smallint(5) unsigned DEFAULT NULL,
  `rank` int(11) NOT NULL,
  `archived` smallint(6) NOT NULL DEFAULT '0',
  `add_when` datetime DEFAULT NULL,
  `add_by` varchar(100) NOT NULL,
  `mod_when` datetime DEFAULT NULL,
  `mod_by` varchar(100) NOT NULL,
  `frameworkId` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `type` (`type`),
  KEY `task_question_id` (`task_question_id`),
  KEY `archived` (`archived`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
Originally created by @warslett on GitHub (Oct 3, 2016). I have an entity that has two ManyToOne associations to two different classes of entity. When the entity is loaded from the repo, both associations are loaded with the **same** class (so one association has the correct class and the other has the wrong class). The other strange behaviour is that the IDs have been switched around so the id for the first association is loaded for the second association and the id of the second association is loaded for the first association. These issues only seem to effect retrieving the entity, creating the entity seems to have no problems and the data this is mapped to was created using the same entities with the same association mappings. The data that gets set in the database when these entities are persisted is all correct, the correct Ids are being set on the correct columns. The only thing I think I'm doing that is unusual in any way is I am using a legacy database schema so the schema is not generated by doctrine and there are some columns on the table that are not mapped with doctrine. My class and mappings without the methods: ``` /** * @ORM\Entity(repositoryClass="AcademyTaskRepository") * @ORM\Table(name="academies__task_questions") * @ORM\HasLifecycleCallbacks */ class AcademyTask implements Owned, TimeStamped { use TimestampTrait, OwnedTrait, ArchiveTrait; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") * @var integer */ private $id; /** * @ORM\ManyToOne(targetEntity="Academy") * @ORM\JoinColumn(name="academy_id", referencedColumnName="id") * @var Academy */ private $academy; /** * @ORM\ManyToOne(targetEntity="CompetencyFrameworkBundle\Entity\Task") * @ORM\JoinColumn(name="task_question_id", referencedColumnName="id") * @var Task */ private $task; /** * @ORM\Column(type="integer", name="time_allowed_override") * @var integer */ private $timeAllowedOverride = 0; } ``` Database: ``` CREATE TABLE `academies__task_questions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `academy_id` int(11) NOT NULL, `task_question_id` int(11) NOT NULL, `type` varchar(10) NOT NULL DEFAULT 'audition', `task_question_override` varchar(255) NOT NULL, `description_override` text NOT NULL, `time_allowed_override` smallint(5) unsigned DEFAULT NULL, `rank` int(11) NOT NULL, `archived` smallint(6) NOT NULL DEFAULT '0', `add_when` datetime DEFAULT NULL, `add_by` varchar(100) NOT NULL, `mod_when` datetime DEFAULT NULL, `mod_by` varchar(100) NOT NULL, `frameworkId` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `type` (`type`), KEY `task_question_id` (`task_question_id`), KEY `archived` (`archived`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; ```
admin closed this issue 2026-01-22 15:03:23 +01:00
Author
Owner

@warslett commented on GitHub (Oct 3, 2016):

An update on this. I seem to get the correct data when I use a repository method to get the offending entities. The problem only seems to occur when I get it from an association:

/**
 * @ORM\Entity(repositoryClass="AcademyRepository")
 * @ORM\Table(name="academies__items")
 * @ORM\HasLifecycleCallbacks
 */
class Academy implements Owned, TimeStamped
{
    use TimestampTrait, OwnedTrait, ArchiveTrait;

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @var integer
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity="AcademyTask", mappedBy="task", cascade={"persist"})
     * @var AcademyTask[]
     */
    private $academyTasks;


    /**
     * @return void
     */
    public function __construct()
    {
        $this->academyTasks = new ArrayCollection();
    }

    /**
     * @return AcademyTask[]
     */
    public function getAcademyTasks(): Collection
    {
        return $this->academyTasks;
    }

    /**
     * @param Task $task
     * @param integer $timeAllowedOverride
     */
    public function addTask(Task $task, $timeAllowedOverride = 0)
    {
        $academyTask = new AcademyTask($this, $task);
        $academyTask->setTimeAllowedOverride($timeAllowedOverride);
        $this->academyTasks->add($academyTask);
    }
}

Then when getAcademyTasks is run I get these weird broken entities back

@warslett commented on GitHub (Oct 3, 2016): An update on this. I seem to get the correct data when I use a repository method to get the offending entities. The problem only seems to occur when I get it from an association: ``` /** * @ORM\Entity(repositoryClass="AcademyRepository") * @ORM\Table(name="academies__items") * @ORM\HasLifecycleCallbacks */ class Academy implements Owned, TimeStamped { use TimestampTrait, OwnedTrait, ArchiveTrait; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") * @var integer */ private $id; /** * @ORM\OneToMany(targetEntity="AcademyTask", mappedBy="task", cascade={"persist"}) * @var AcademyTask[] */ private $academyTasks; /** * @return void */ public function __construct() { $this->academyTasks = new ArrayCollection(); } /** * @return AcademyTask[] */ public function getAcademyTasks(): Collection { return $this->academyTasks; } /** * @param Task $task * @param integer $timeAllowedOverride */ public function addTask(Task $task, $timeAllowedOverride = 0) { $academyTask = new AcademyTask($this, $task); $academyTask->setTimeAllowedOverride($timeAllowedOverride); $this->academyTasks->add($academyTask); } } ``` Then when getAcademyTasks is run I get these weird broken entities back
Author
Owner

@lcobucci commented on GitHub (Oct 3, 2016):

@warslett that sounds weird... it looks like a mapping issue but I'm not 100% sure. Could you try to reproduce this behavior on a functional test? You can use these tests as reference.

You don't need necessarily to fix the issue but sending a PR with the tests would be really helpful.

@lcobucci commented on GitHub (Oct 3, 2016): @warslett that sounds weird... it looks like a mapping issue but I'm not 100% sure. Could you try to reproduce this behavior on a functional test? You can use [these tests](https://github.com/doctrine/doctrine2/tree/cd11723e6310b5973f50e7d9819c58916721d290/tests/Doctrine/Tests/ORM/Functional/Ticket) as reference. You don't need necessarily to fix the issue but sending a PR with the tests would be really helpful.
Author
Owner

@warslett commented on GitHub (Oct 3, 2016):

@lcobucci I'll try and get a pull request sent in the next few days

@warslett commented on GitHub (Oct 3, 2016): @lcobucci I'll try and get a pull request sent in the next few days
Author
Owner

@lcobucci commented on GitHub (Oct 3, 2016):

Cool, thanks! Ping me if need any help to create the test

@lcobucci commented on GitHub (Oct 3, 2016): Cool, thanks! Ping me if need any help to create the test
Author
Owner

@warslett commented on GitHub (Nov 1, 2016):

Sorry, solved this ages ago but never closed this issue. The problem was due to user error I'm afraid. The academy entity was mapped incorrectly

    /**
     * @ORM\OneToMany(targetEntity="AcademyTask", mappedBy="task", cascade={"persist"})
     * @var AcademyTask[]
     */
    private $academyTasks;

Note mappedBy="task" should have been mappedBy="academy". The issue wasn't obvious because it was loading the AcademyTask association, just with the wrong data which is why we were expecting the issue to be with the mappings on the AcademyTask entity and after checking them several times concluded that there was nothing wrong with our code.

I suppose it would be more useful if Doctrine could validate this kind of thing and throw a useful error. the association on the Academy mapped by task can never be valid because the task property is not of type Academy.

@warslett commented on GitHub (Nov 1, 2016): Sorry, solved this ages ago but never closed this issue. The problem was due to user error I'm afraid. The academy entity was mapped incorrectly ``` /** * @ORM\OneToMany(targetEntity="AcademyTask", mappedBy="task", cascade={"persist"}) * @var AcademyTask[] */ private $academyTasks; ``` Note mappedBy="task" should have been mappedBy="academy". The issue wasn't obvious because it was loading the AcademyTask association, just with the wrong data which is why we were expecting the issue to be with the mappings on the AcademyTask entity and after checking them several times concluded that there was nothing wrong with our code. I suppose it would be more useful if Doctrine could validate this kind of thing and throw a useful error. the association on the Academy mapped by task can never be valid because the task property is not of type Academy.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5282