migrations icon indicating copy to clipboard operation
migrations copied to clipboard

Autogenerated migration keeps dropping and re-adding unrelated foreign keys

Open Menelion opened this issue 9 months ago • 2 comments

Bug Report

Q A
BC Break no
Version 3.8.2

Summary

I'm using Symfony 7.2 and its Maker bundle. Under the hood the make:migration command calls doctrine:migrations:diff. For some reason, it keeps dropping and re-adding foreign keys on totally unrelated entities. I'm using MariaDB 11.2.2 on this machine.

Current behavior

doctrine:migrations:diff drops and re-adds unrelated foreign keys (see steps to reproduce).

How to reproduce

  1. Either use make:entity in Symfony or add this entity manually:
<?php

namespace App\Entity;

use App\Repository\LanguageRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

#[ORM\Entity(repositoryClass: LanguageRepository::class)]
#[ORM\Table(name: 'languages')]
#[UniqueEntity(fields: ['code'], message: 'There is already a language with this code')]
class Language
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(type: Types::SMALLINT, options: ['unsigned' => true])]
    private ?int $position = null;

    #[ORM\Column(length: 20)]
    private ?string $code = null;

    #[ORM\Column(length: 100)]
    private ?string $englishName = null;

    #[ORM\Column(length: 100)]
    private ?string $nativeName = null;

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

    public function getPosition(): ?int
    {
        return $this->position;
    }

    public function setPosition(int $position): self
    {
        $this->position = $position;

        return $this;
    }

    public function getCode(): ?string
    {
        return $this->code;
    }

    public function setCode(string $code): self
    {
        $this->code = $code;

        return $this;
    }

    public function getEnglishName(): ?string
    {
        return $this->englishName;
    }

    public function setEnglishName(string $englishName): static
    {
        $this->englishName = $englishName;

        return $this;
    }

    public function getNativeName(): ?string
    {
        return $this->nativeName;
    }

    public function setNativeName(string $nativeName): static
    {
        $this->nativeName = $nativeName;

        return $this;
    }
}
  1. Run doctrine:migrations:diff --formatted. I'm getting this:
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20250109223941 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Created languages table';
    }

    public function up(Schema $schema): void
    {
        $this->addSql('CREATE TABLE languages (
          id INT AUTO_INCREMENT NOT NULL,
          position SMALLINT UNSIGNED NOT NULL,
          code VARCHAR(20) NOT NULL,
          english_name VARCHAR(100) NOT NULL,
          native_name VARCHAR(100) NOT NULL,
          PRIMARY KEY(id)
        ) DEFAULT CHARACTER
        SET
          utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
        $this->addSql('ALTER TABLE reset_password_request DROP FOREIGN KEY FK_7CE748AA76ED395');
        $this->addSql('ALTER TABLE
           reset_password_request
        ADD
          CONSTRAINT FK_7CE748AA76ED395 FOREIGN KEY (user_id) REFERENCES users (id)');
    }

    public function down(Schema $schema): void
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->addSql('DROP TABLE languages');
        $this->addSql('ALTER TABLE reset_password_request DROP FOREIGN KEY FK_7CE748AA76ED395');
        $this->addSql('ALTER TABLE
          reset_password_request
        ADD
          CONSTRAINT FK_7CE748AA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE');
    }
}

the reset_password_request migration for reference:

<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20250101232946 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Added the reset password request table and isVerified column to the Users table';
    }

    public function up(Schema $schema): void
    {
        $this->addSql(
            'CREATE TABLE reset_password_request (
              id INT AUTO_INCREMENT NOT NULL,
              user_id INT NOT NULL,
              selector VARCHAR(20) NOT NULL,
              hashed_token VARCHAR(100) NOT NULL,
              requested_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\',
              expires_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\',
              INDEX IDX_7CE748AA76ED395 (user_id),
              PRIMARY KEY(id)
            )
            DEFAULT CHARACTER SET utf8mb4
            COLLATE `utf8mb4_unicode_ci`
            ENGINE = InnoDB
        ');

        $this->addSql(
            'ALTER TABLE
            reset_password_request
            ADD
            CONSTRAINT FK_7CE748AA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
        ');

        $this->addSql('ALTER TABLE users ADD is_verified TINYINT(1) NOT NULL');
    }

    public function down(Schema $schema): void
    {
        $this->addSql('ALTER TABLE reset_password_request DROP FOREIGN KEY FK_7CE748AA76ED395');
        $this->addSql('DROP TABLE reset_password_request');
        $this->addSql('ALTER TABLE users DROP is_verified');
    }
}

Expected behavior

The reset_password_request table has nothing to do with this one at all, so it shouldn't be altered. There is no relations to users table either, it's just a standalone languages table.

Menelion avatar Jan 09 '25 23:01 Menelion