PHP attributes usage cause unexpected changes #6896

Open
opened 2026-01-22 15:40:52 +01:00 by admin · 0 comments
Owner

Originally created by @mpiot on GitHub (Jan 7, 2022).

Bug Report

Q A
BC Break
Version 2.10.4

Summary

In a project I try to update from Doctrine Annotations to PHP Attributes, but it causing unexpected schema changes. (Replace the doctrine/migrations#1222)

Current behavior

Previous annotations:

/**
 * @ORM\ManyToMany(targetEntity=User::class)
 * @ORM\JoinColumn(nullable=false)
 */
private Collection $inventors;

New attributes:

#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: false)]
private Collection $inventors;

When executing a migrations we have something like:

public function up(Schema $schema): void
 {
    // this up() migration is auto-generated, please modify it to your needs
    $this->addSql('ALTER TABLE soleau_envelope_user DROP CONSTRAINT FK_A06D2FACA2F59D5');
    $this->addSql('ALTER TABLE soleau_envelope_user ADD CONSTRAINT FK_A06D2FACA2F59D5 FOREIGN KEY (soleau_envelope_id) REFERENCES soleau_envelope (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}

public function down(Schema $schema): void
{
    // this down() migration is auto-generated, please modify it to your needs
    $this->addSql('ALTER TABLE soleau_envelope_user DROP CONSTRAINT fk_a06d2faca2f59d5');
    $this->addSql('ALTER TABLE soleau_envelope_user ADD CONSTRAINT fk_a06d2faca2f59d5 FOREIGN KEY (soleau_envelope_id) REFERENCES soleau_envelope (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}

If I dump fromSchema and toSchema from the Doctrine/Migration DiffGenerator, I have something like:

Before:

Doctrine\DBAL\Schema\Table^ {#90
  #_name: "soleau_envelope_user"
  #_namespace: null
  #_quoted: false
  #_columns: array:2 [
    // ...
  ]
  #_indexes: array:3 [
    "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#106
      #_name: "idx_a06d2faca2f59d5"
      #_namespace: null
      #_quoted: false
      #_columns: array:1 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#107
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: false
      #_isPrimary: false
      #_flags: []
      -options: array:1 [
        "lengths" => array:1 [
          0 => null
        ]
      ]
    }
    "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#108
      #_name: "idx_a06d2faa76ed395"
      #_namespace: null
      #_quoted: false
      #_columns: array:1 [
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#73
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: false
      #_isPrimary: false
      #_flags: []
      -options: array:1 [
        "lengths" => array:1 [
          0 => null
        ]
      ]
    }
    "soleau_envelope_user_pkey" => Doctrine\DBAL\Schema\Index^ {#91
      #_name: "soleau_envelope_user_pkey"
      #_namespace: null
      #_quoted: false
      #_columns: array:2 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#93
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#62
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: true
      #_isPrimary: true
      #_flags: []
      -options: array:1 [
        "lengths" => array:2 [
          0 => null
          1 => null
        ]
      ]
    }
  ]
  #_primaryKeyName: "soleau_envelope_user_pkey"
  #uniqueConstraints: []
  #_fkConstraints: array:2 [
    "fk_a06d2faa76ed395" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#123
      #_name: "fk_a06d2faa76ed395"
      #_namespace: null
      #_quoted: false
      #_localTable: Doctrine\DBAL\Schema\Table^ {#90}
      #_localColumnNames: array:1 [
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#117
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#116
        #_name: "app_user"
        #_namespace: null
        #_quoted: false
      }
      #_foreignColumnNames: array:1 [
        "id" => Doctrine\DBAL\Schema\Identifier^ {#113
          #_name: "id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_options: array:2 [
        "onUpdate" => null
        "onDelete" => "CASCADE"
      ]
    }
    "fk_a06d2faca2f59d5" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#112
      #_name: "fk_a06d2faca2f59d5"
      #_namespace: null
      #_quoted: false
      #_localTable: Doctrine\DBAL\Schema\Table^ {#90}
      #_localColumnNames: array:1 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#111
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#110
        #_name: "soleau_envelope"
        #_namespace: null
        #_quoted: false
      }
      #_foreignColumnNames: array:1 [
        "id" => Doctrine\DBAL\Schema\Identifier^ {#109
          #_name: "id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_options: array:2 [
        "onUpdate" => null
        "onDelete" => "CASCADE"
      ]
    }
  ]
  #_options: array:2 [
    "create_options" => []
    "comment" => null
  ]
  #_schemaConfig: Doctrine\DBAL\Schema\SchemaConfig^ {#10711
    #hasExplicitForeignKeyIndexes: false
    #maxIdentifierLength: 63
    #name: "symfony"
    #defaultTableOptions: array:1 [
      "charset" => "utf8"
    ]
  }
  -implicitIndexes: []
}

After (with attributes):

^ Doctrine\DBAL\Schema\Table^ {#20659
  #_name: "soleau_envelope_user"
  #_namespace: null
  #_quoted: false
  #_columns: array:2 [
    // ...
  ]
  #_indexes: array:3 [
    "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#20665
      #_name: "IDX_A06D2FACA2F59D5"
      #_namespace: null
      #_quoted: false
      #_columns: array:1 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20666
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: false
      #_isPrimary: false
      #_flags: []
      -options: []
    }
    "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#20672
      #_name: "IDX_A06D2FAA76ED395"
      #_namespace: null
      #_quoted: false
      #_columns: array:1 [
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20673
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: false
      #_isPrimary: false
      #_flags: []
      -options: []
    }
    "primary" => Doctrine\DBAL\Schema\Index^ {#20674
      #_name: "primary"
      #_namespace: null
      #_quoted: false
      #_columns: array:2 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20675
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20676
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_isUnique: true
      #_isPrimary: true
      #_flags: []
      -options: []
    }
  ]
  #_primaryKeyName: "primary"
  #uniqueConstraints: []
  #_fkConstraints: array:2 [
    "fk_a06d2faca2f59d5" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#20661
      #_name: "FK_A06D2FACA2F59D5"
      #_namespace: null
      #_quoted: false
      #_localTable: Doctrine\DBAL\Schema\Table^ {#20659}
      #_localColumnNames: array:1 [
        "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20662
          #_name: "soleau_envelope_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#20663
        #_name: "soleau_envelope"
        #_namespace: null
        #_quoted: false
      }
      #_foreignColumnNames: array:1 [
        "id" => Doctrine\DBAL\Schema\Identifier^ {#20664
          #_name: "id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_options: []
    }
    "fk_a06d2faa76ed395" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#20668
      #_name: "FK_A06D2FAA76ED395"
      #_namespace: null
      #_quoted: false
      #_localTable: Doctrine\DBAL\Schema\Table^ {#20659}
      #_localColumnNames: array:1 [
        "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20669
          #_name: "user_id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#20670
        #_name: "app_user"
        #_namespace: null
        #_quoted: false
      }
      #_foreignColumnNames: array:1 [
        "id" => Doctrine\DBAL\Schema\Identifier^ {#20671
          #_name: "id"
          #_namespace: null
          #_quoted: false
        }
      ]
      #_options: array:1 [
        "onDelete" => "CASCADE"
      ]
    }
  ]
  #_options: array:2 [
    "create_options" => []
    "charset" => "utf8"
  ]
  #_schemaConfig: Doctrine\DBAL\Schema\SchemaConfig^ {#14544
    #hasExplicitForeignKeyIndexes: false
    #maxIdentifierLength: 63
    #name: "symfony"
    #defaultTableOptions: array:1 [
      "charset" => "utf8"
    ]
  }
  -implicitIndexes: array:2 [
    "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#20665}
    "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#20672}
  ]
}

Expected behavior

By changing from DoctrineAnnotations to PHP Attributes, this change should not appear. This appear 10 times for 51 ManyToMany relations.

The change is always the same, we loose the 'ON DELETE CASCADE' and the constraint name is uppercase.

Originally created by @mpiot on GitHub (Jan 7, 2022). ### Bug Report | Q | A |------------ | ------ | BC Break | | Version | 2.10.4 #### Summary In a project I try to update from Doctrine Annotations to PHP Attributes, but it causing unexpected schema changes. (Replace the doctrine/migrations#1222) #### Current behavior Previous annotations: ```php /** * @ORM\ManyToMany(targetEntity=User::class) * @ORM\JoinColumn(nullable=false) */ private Collection $inventors; ``` New attributes: ```php #[ORM\ManyToMany(targetEntity: User::class)] #[ORM\JoinColumn(nullable: false)] private Collection $inventors; ``` When executing a migrations we have something like: ```php public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE soleau_envelope_user DROP CONSTRAINT FK_A06D2FACA2F59D5'); $this->addSql('ALTER TABLE soleau_envelope_user ADD CONSTRAINT FK_A06D2FACA2F59D5 FOREIGN KEY (soleau_envelope_id) REFERENCES soleau_envelope (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE soleau_envelope_user DROP CONSTRAINT fk_a06d2faca2f59d5'); $this->addSql('ALTER TABLE soleau_envelope_user ADD CONSTRAINT fk_a06d2faca2f59d5 FOREIGN KEY (soleau_envelope_id) REFERENCES soleau_envelope (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); } ``` If I dump fromSchema and toSchema from the Doctrine/Migration DiffGenerator, I have something like: Before: ``` Doctrine\DBAL\Schema\Table^ {#90 #_name: "soleau_envelope_user" #_namespace: null #_quoted: false #_columns: array:2 [ // ... ] #_indexes: array:3 [ "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#106 #_name: "idx_a06d2faca2f59d5" #_namespace: null #_quoted: false #_columns: array:1 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#107 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } ] #_isUnique: false #_isPrimary: false #_flags: [] -options: array:1 [ "lengths" => array:1 [ 0 => null ] ] } "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#108 #_name: "idx_a06d2faa76ed395" #_namespace: null #_quoted: false #_columns: array:1 [ "user_id" => Doctrine\DBAL\Schema\Identifier^ {#73 #_name: "user_id" #_namespace: null #_quoted: false } ] #_isUnique: false #_isPrimary: false #_flags: [] -options: array:1 [ "lengths" => array:1 [ 0 => null ] ] } "soleau_envelope_user_pkey" => Doctrine\DBAL\Schema\Index^ {#91 #_name: "soleau_envelope_user_pkey" #_namespace: null #_quoted: false #_columns: array:2 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#93 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } "user_id" => Doctrine\DBAL\Schema\Identifier^ {#62 #_name: "user_id" #_namespace: null #_quoted: false } ] #_isUnique: true #_isPrimary: true #_flags: [] -options: array:1 [ "lengths" => array:2 [ 0 => null 1 => null ] ] } ] #_primaryKeyName: "soleau_envelope_user_pkey" #uniqueConstraints: [] #_fkConstraints: array:2 [ "fk_a06d2faa76ed395" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#123 #_name: "fk_a06d2faa76ed395" #_namespace: null #_quoted: false #_localTable: Doctrine\DBAL\Schema\Table^ {#90} #_localColumnNames: array:1 [ "user_id" => Doctrine\DBAL\Schema\Identifier^ {#117 #_name: "user_id" #_namespace: null #_quoted: false } ] #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#116 #_name: "app_user" #_namespace: null #_quoted: false } #_foreignColumnNames: array:1 [ "id" => Doctrine\DBAL\Schema\Identifier^ {#113 #_name: "id" #_namespace: null #_quoted: false } ] #_options: array:2 [ "onUpdate" => null "onDelete" => "CASCADE" ] } "fk_a06d2faca2f59d5" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#112 #_name: "fk_a06d2faca2f59d5" #_namespace: null #_quoted: false #_localTable: Doctrine\DBAL\Schema\Table^ {#90} #_localColumnNames: array:1 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#111 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } ] #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#110 #_name: "soleau_envelope" #_namespace: null #_quoted: false } #_foreignColumnNames: array:1 [ "id" => Doctrine\DBAL\Schema\Identifier^ {#109 #_name: "id" #_namespace: null #_quoted: false } ] #_options: array:2 [ "onUpdate" => null "onDelete" => "CASCADE" ] } ] #_options: array:2 [ "create_options" => [] "comment" => null ] #_schemaConfig: Doctrine\DBAL\Schema\SchemaConfig^ {#10711 #hasExplicitForeignKeyIndexes: false #maxIdentifierLength: 63 #name: "symfony" #defaultTableOptions: array:1 [ "charset" => "utf8" ] } -implicitIndexes: [] } ``` After (with attributes): ``` ^ Doctrine\DBAL\Schema\Table^ {#20659 #_name: "soleau_envelope_user" #_namespace: null #_quoted: false #_columns: array:2 [ // ... ] #_indexes: array:3 [ "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#20665 #_name: "IDX_A06D2FACA2F59D5" #_namespace: null #_quoted: false #_columns: array:1 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20666 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } ] #_isUnique: false #_isPrimary: false #_flags: [] -options: [] } "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#20672 #_name: "IDX_A06D2FAA76ED395" #_namespace: null #_quoted: false #_columns: array:1 [ "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20673 #_name: "user_id" #_namespace: null #_quoted: false } ] #_isUnique: false #_isPrimary: false #_flags: [] -options: [] } "primary" => Doctrine\DBAL\Schema\Index^ {#20674 #_name: "primary" #_namespace: null #_quoted: false #_columns: array:2 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20675 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20676 #_name: "user_id" #_namespace: null #_quoted: false } ] #_isUnique: true #_isPrimary: true #_flags: [] -options: [] } ] #_primaryKeyName: "primary" #uniqueConstraints: [] #_fkConstraints: array:2 [ "fk_a06d2faca2f59d5" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#20661 #_name: "FK_A06D2FACA2F59D5" #_namespace: null #_quoted: false #_localTable: Doctrine\DBAL\Schema\Table^ {#20659} #_localColumnNames: array:1 [ "soleau_envelope_id" => Doctrine\DBAL\Schema\Identifier^ {#20662 #_name: "soleau_envelope_id" #_namespace: null #_quoted: false } ] #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#20663 #_name: "soleau_envelope" #_namespace: null #_quoted: false } #_foreignColumnNames: array:1 [ "id" => Doctrine\DBAL\Schema\Identifier^ {#20664 #_name: "id" #_namespace: null #_quoted: false } ] #_options: [] } "fk_a06d2faa76ed395" => Doctrine\DBAL\Schema\ForeignKeyConstraint^ {#20668 #_name: "FK_A06D2FAA76ED395" #_namespace: null #_quoted: false #_localTable: Doctrine\DBAL\Schema\Table^ {#20659} #_localColumnNames: array:1 [ "user_id" => Doctrine\DBAL\Schema\Identifier^ {#20669 #_name: "user_id" #_namespace: null #_quoted: false } ] #_foreignTableName: Doctrine\DBAL\Schema\Identifier^ {#20670 #_name: "app_user" #_namespace: null #_quoted: false } #_foreignColumnNames: array:1 [ "id" => Doctrine\DBAL\Schema\Identifier^ {#20671 #_name: "id" #_namespace: null #_quoted: false } ] #_options: array:1 [ "onDelete" => "CASCADE" ] } ] #_options: array:2 [ "create_options" => [] "charset" => "utf8" ] #_schemaConfig: Doctrine\DBAL\Schema\SchemaConfig^ {#14544 #hasExplicitForeignKeyIndexes: false #maxIdentifierLength: 63 #name: "symfony" #defaultTableOptions: array:1 [ "charset" => "utf8" ] } -implicitIndexes: array:2 [ "idx_a06d2faca2f59d5" => Doctrine\DBAL\Schema\Index^ {#20665} "idx_a06d2faa76ed395" => Doctrine\DBAL\Schema\Index^ {#20672} ] } ``` #### Expected behavior By changing from DoctrineAnnotations to PHP Attributes, this change should not appear. This appear 10 times for 51 ManyToMany relations. The change is always the same, we loose the 'ON DELETE CASCADE' and the constraint name is uppercase.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6896