Why is Id AutoGenerated anew on UPDATE with Relation using Symfony Doctrine? #6843

Closed
opened 2026-01-22 15:39:52 +01:00 by admin · 1 comment
Owner

Originally created by @dVVIIb on GitHub (Oct 4, 2021).

Bug Report

Q A
BC Break yes/no?
Version 2.4
Platform Windows 10

Summary

This has been rather difficult to diagnose especially as I was dealing with some complicated layered code. If I was sure, I would have filed a bug with Doctrine before now, but I wanted to first make sure I'm not making some glaring mistake or such, in implementation, so I posted a question on StackOverflow, first, but still have no answer.

I have painstakingly tried to reduce the code to a simplified working example. On my test database tables, there are additional columns that are not referred to in the demo code.

Current behavior

// src/Entity/Record.php
declare(strict_types = 1);
namespace App\Entity;

use App\Repository\RecordRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=RecordRepository::class)
 * @ORM\Table(name="Records")
 */
class Record {
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    public function getId(): ?int {
        return $this->id;
    }

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\RecordStatus", mappedBy="record", cascade={"persist"})
     * @ORM\JoinColumn(name="Id")
     */
    private ?RecordStatus $recordStatus = NULL;

    public function getRecordStatus(): ?RecordStatus {
        return $this->recordStatus;
    }

    public function setRecordStatus(RecordStatus $value): void {
        $value->setRecord($this);
        $this->recordStatus = $value;
    }
}

// src/Entity/RecordStatus.php
declare(strict_types = 1);
namespace App\Entity;

use App\Repository\RecordStatusRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=RecordStatusRepository::class)
 * @ORM\Table(name="Record_Statuses")
 */
class RecordStatus {
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    public function getId(): ?int {
        return $this->id;
    }

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\Record", inversedBy="record")
     * @ORM\JoinColumn(name="RecordId")
     */
    private Record $record;

    public function getRecord(): Record {
        return $this->record;
    }

    public function setRecord(Record $value): void {
        $this->record = $value;
    }
}

// src/Controller/DefaultController.php
declare(strict_types = 1);
namespace App\Controller;

use App\Entity\Record;
use App\Entity\RecordStatus;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends AbstractController {
    /**
     * @Route("/")
     */
    public function home(): Response {
        $doctrine = $this->getDoctrine();
        $entityManager = $doctrine->getManager();
        $recordRepository = $doctrine->getRepository(Record::class);
        $recordStatus = new RecordStatus;
        $item = $recordRepository->find(1);
        $item->setRecordStatus($recordStatus);
        $entityManager->flush();
        return new JsonResponse(['id' => $item->getId()]);
    }
}

When this route ("/") is triggered, the record is updated and the old Id is displayed. But after the update, checking the database shows that the record now has a newly Auto-Generated Id, that comes after the last previous record in the table. Note that this is on UPDATE and not INSERT. The above is, as shown, OneToOne. I haven't yet checked whether the same issue occurs for OneToMany/ManyToOne Associations.

Of course, I realize this occurs because I'm using a new Associated child entity here, and can work around this by using an existing child object if any, but there may be a use case for a new child association (esp. if one wasn't assigned on persisting new parent Record), depending on application requirements, so a proper solution is needed anyway, I think.

How to reproduce

Please see above code samples and below:

CREATE TABLE Records (
	Id bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
	, CreatedAt timestamp NOT NULL DEFAULT NOW()
	, CreatedBy int UNSIGNED NOT NULL
	, Description varchar(4094) COLLATE latin1_general_ci NOT NULL
) CHARSET=latin1 COLLATE=latin1_bin;

CREATE TABLE Record_Statuses (
	Id bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
	, RecordId bigint UNSIGNED NOT NULL
	, CreatedAt timestamp NOT NULL DEFAULT NOW()
	, UpdatedAt timestamp NOT NULL DEFAULT NOW()
	, CreatedBy int UNSIGNED NOT NULL
	, UpdatedBy int UNSIGNED NOT NULL
	, RecordStateId tinyint UNSIGNED NOT NULL
) CHARSET=latin1 COLLATE=latin1_bin;

These tables and above code are not completely compatible AS IS. Shown here in full, if necessary to help resolve the issue or if relevant in any way. You may need to exclude some columns, or add dummy columns to the entities in above code.

Expected behavior

(Record/row) Id should not be altered during update.

One urgent project of mine is held up due to this issue. Any assistance/clarification is appreciated.

Thank you.

Originally created by @dVVIIb on GitHub (Oct 4, 2021). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | yes/no? | Version | 2.4 | Platform | Windows 10 #### Summary This has been rather difficult to diagnose especially as I was dealing with some complicated layered code. If I was sure, I would have filed a bug with Doctrine before now, but I wanted to first make sure I'm not making some glaring mistake or such, in implementation, so I posted a question on [StackOverflow](https://stackoverflow.com/questions/69402807/why-is-id-autogenerated-anew-on-update-with-relation-using-symfony-doctrine), first, but still have no answer. I have painstakingly tried to reduce the code to a simplified working example. On my test database tables, there are additional columns that are not referred to in the demo code. #### Current behavior ``` // src/Entity/Record.php declare(strict_types = 1); namespace App\Entity; use App\Repository\RecordRepository; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=RecordRepository::class) * @ORM\Table(name="Records") */ class Record { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ protected $id; public function getId(): ?int { return $this->id; } /** * @ORM\OneToOne(targetEntity="App\Entity\RecordStatus", mappedBy="record", cascade={"persist"}) * @ORM\JoinColumn(name="Id") */ private ?RecordStatus $recordStatus = NULL; public function getRecordStatus(): ?RecordStatus { return $this->recordStatus; } public function setRecordStatus(RecordStatus $value): void { $value->setRecord($this); $this->recordStatus = $value; } } // src/Entity/RecordStatus.php declare(strict_types = 1); namespace App\Entity; use App\Repository\RecordStatusRepository; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=RecordStatusRepository::class) * @ORM\Table(name="Record_Statuses") */ class RecordStatus { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ protected $id; public function getId(): ?int { return $this->id; } /** * @ORM\OneToOne(targetEntity="App\Entity\Record", inversedBy="record") * @ORM\JoinColumn(name="RecordId") */ private Record $record; public function getRecord(): Record { return $this->record; } public function setRecord(Record $value): void { $this->record = $value; } } // src/Controller/DefaultController.php declare(strict_types = 1); namespace App\Controller; use App\Entity\Record; use App\Entity\RecordStatus; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class DefaultController extends AbstractController { /** * @Route("/") */ public function home(): Response { $doctrine = $this->getDoctrine(); $entityManager = $doctrine->getManager(); $recordRepository = $doctrine->getRepository(Record::class); $recordStatus = new RecordStatus; $item = $recordRepository->find(1); $item->setRecordStatus($recordStatus); $entityManager->flush(); return new JsonResponse(['id' => $item->getId()]); } } ``` When this route ("/") is triggered, the record is updated and the old Id is displayed. But after the update, checking the database shows that the record now has a newly Auto-Generated Id, that comes after the last previous record in the table. Note that this is on UPDATE and not INSERT. The above is, as shown, OneToOne. I haven't yet checked whether the same issue occurs for OneToMany/ManyToOne Associations. Of course, I realize this occurs because I'm using a new Associated child entity here, and can work around this by using an existing child object if any, but there may be a use case for a new child association (esp. if one wasn't assigned on persisting new parent Record), depending on application requirements, so a proper solution is needed anyway, I think. #### How to reproduce Please see above code samples and below: ``` CREATE TABLE Records ( Id bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , CreatedAt timestamp NOT NULL DEFAULT NOW() , CreatedBy int UNSIGNED NOT NULL , Description varchar(4094) COLLATE latin1_general_ci NOT NULL ) CHARSET=latin1 COLLATE=latin1_bin; CREATE TABLE Record_Statuses ( Id bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , RecordId bigint UNSIGNED NOT NULL , CreatedAt timestamp NOT NULL DEFAULT NOW() , UpdatedAt timestamp NOT NULL DEFAULT NOW() , CreatedBy int UNSIGNED NOT NULL , UpdatedBy int UNSIGNED NOT NULL , RecordStateId tinyint UNSIGNED NOT NULL ) CHARSET=latin1 COLLATE=latin1_bin; ``` These tables and above code are not completely compatible AS IS. Shown here in full, if necessary to help resolve the issue or if relevant in any way. You may need to exclude some columns, or add dummy columns to the entities in above code. #### Expected behavior (Record/row) Id should not be altered during update. One urgent project of mine is held up due to this issue. Any assistance/clarification is appreciated. Thank you.
admin closed this issue 2026-01-22 15:39:52 +01:00
Author
Owner

@derrabus commented on GitHub (Oct 4, 2021):

I'm moving this issue to discussions because we have stopped providing bugfix support for ORM 2.4 six years ago.

@derrabus commented on GitHub (Oct 4, 2021): I'm moving this issue to discussions because we have stopped providing bugfix support for ORM 2.4 six years ago.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6843