Skip to content

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

@dVVIIb

Description

@dVVIIb

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions