Unique Constraints etc. defined on STI entities are ignored #7213

Open
opened 2026-01-22 15:47:04 +01:00 by admin · 8 comments
Owner

Originally created by @simPod on GitHub (Aug 10, 2023).

Bug Report

Q A
BC Break no
Version 2.16

Summary

This condition aborts processing of STI entities when creating a Schema 597a63a86c/lib/Doctrine/ORM/Tools/SchemaTool.php (L140)

(it would require more changes in order to make it work)

Current behavior

Unique constraints are ignored, ( [ 'a', 'b' ] in example bellow)

How to reproduce

#[ORM\Entity]
#[ORM\Table(name: 't')]
#[ORM\InheritanceType(value: 'SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)]
#[ORM\DiscriminatorMap(value: self::EntityMap)]
abstract class A {...}

#[ORM\Entity]
#[ORM\Table(name: 't')]
#[ORM\UniqueConstraint(
    fields: [ 'a', 'b' ],
)]
class B extends A {...}

Expected behavior

Not ignored. [ 'a', 'b' ] UC is created.

Originally created by @simPod on GitHub (Aug 10, 2023). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | no | Version | 2.16 #### Summary This condition aborts processing of STI entities when creating a Schema https://github.com/doctrine/orm/blob/597a63a86ca8c5f9d1ec2dc74fe3d1269d43434a/lib/Doctrine/ORM/Tools/SchemaTool.php#L140 (it would require more changes in order to make it work) #### Current behavior Unique constraints are ignored, ( `[ 'a', 'b' ]` in example bellow) #### How to reproduce ```php #[ORM\Entity] #[ORM\Table(name: 't')] #[ORM\InheritanceType(value: 'SINGLE_TABLE')] #[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)] #[ORM\DiscriminatorMap(value: self::EntityMap)] abstract class A {...} #[ORM\Entity] #[ORM\Table(name: 't')] #[ORM\UniqueConstraint( fields: [ 'a', 'b' ], )] class B extends A {...} ``` #### Expected behavior Not ignored. `[ 'a', 'b' ]` UC is created.
admin added the Won't Fix label 2026-01-22 15:47:04 +01:00
Author
Owner

@derrabus commented on GitHub (Aug 10, 2023):

Why don't you declare the UniqueConstraint on class A?

@derrabus commented on GitHub (Aug 10, 2023): Why don't you declare the `UniqueConstraint` on class `A`?
Author
Owner

@simPod commented on GitHub (Aug 10, 2023):

Because the UniqueConstraint is related to B, not A.

B has fields contained in unique constraint that are not present on A.

@simPod commented on GitHub (Aug 10, 2023): Because the UniqueConstraint is related to `B`, not `A`. `B` has fields contained in unique constraint that are not present on `A`.
Author
Owner

@derrabus commented on GitHub (Aug 10, 2023):

I see. But would it work?

I mean, all child entities of A will be stored in A's table. That means any constraint applies to all of A's children. You cannot have a constraint that only applies to B instances.

@derrabus commented on GitHub (Aug 10, 2023): I see. But would it work? I mean, all child entities of `A` will be stored in A's table. That means any constraint applies to all of A's children. You cannot have a constraint that only applies to `B` instances.
Author
Owner

@simPod commented on GitHub (Aug 10, 2023):

Yup, it will.

Because B has unique fields that other children do not have. So having a UC with those fields on common table will not affect other children as they will have null value in those fields.

E.g.

  • B has field foo
  • A nor other children has no such field
  • UC is created on field foo
  • B has values in foo field in DB
  • other entities in inheritance bundle have null values in foo field

In code term I want this

#[ORM\Entity]
#[ORM\Table(name: 't')]
#[ORM\InheritanceType(value: 'SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)]
#[ORM\DiscriminatorMap(value: self::EntityMap)]
abstract A {
}

#[ORM\Entity]
#[ORM\Table(name: 't')]
#[ORM\UniqueConstraint(fields: [ 'foo' ])]
class B extends A {
   #[ORM\...]
   private string $foo;
}

But currently I have to do this

#[ORM\Entity]
#[ORM\Table(name: 't')]
#[ORM\InheritanceType(value: 'SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)]
#[ORM\DiscriminatorMap(value: self::EntityMap)]
#[ORM\UniqueConstraint(columns: [ 'foo' ])]
abstract A {
}

#[ORM\Entity]
class B extends A {
   #[ORM\...]
   private string $foo;
}

which works ->

I see. But would it work?

so yes.

@simPod commented on GitHub (Aug 10, 2023): Yup, it will. Because `B` has unique fields that other children do not have. So having a UC with those fields on common table will not affect other children as they will have `null` value in those fields. E.g. - `B` has field `foo` - `A` nor other children has no such field - UC is created on field `foo` - B has values in `foo` field in DB - other entities in inheritance bundle have `null` values in `foo` field In code term I want this ```php #[ORM\Entity] #[ORM\Table(name: 't')] #[ORM\InheritanceType(value: 'SINGLE_TABLE')] #[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)] #[ORM\DiscriminatorMap(value: self::EntityMap)] abstract A { } #[ORM\Entity] #[ORM\Table(name: 't')] #[ORM\UniqueConstraint(fields: [ 'foo' ])] class B extends A { #[ORM\...] private string $foo; } ``` But currently I have to do this ```php #[ORM\Entity] #[ORM\Table(name: 't')] #[ORM\InheritanceType(value: 'SINGLE_TABLE')] #[ORM\DiscriminatorColumn(name: 'entity_type', type: Types::STRING)] #[ORM\DiscriminatorMap(value: self::EntityMap)] #[ORM\UniqueConstraint(columns: [ 'foo' ])] abstract A { } #[ORM\Entity] class B extends A { #[ORM\...] private string $foo; } ``` which works -> > I see. But would it work? so yes.
Author
Owner

@derrabus commented on GitHub (Aug 10, 2023):

Yup, it will.

Perfect. 🙂

But currently I have to do this

And that looks perfectly fine to me. With single-table inheritance, the root entity controls the table and the children can only add nullable fields to it. Those settings have to go somewhere and unless we have a very good reason to, I would not add the complexity of scraping and merging table settings from all children.

What we can do here:

  • Improve the documentation if it hasn't been clear for you
  • Trigger a deprecation that we turn into an exception in 3.0 if Table, UniqueConstraint and friends are used on children in a single-table inheritance.
@derrabus commented on GitHub (Aug 10, 2023): > Yup, it will. Perfect. 🙂 > But currently I have to do this And that looks perfectly fine to me. With single-table inheritance, the root entity controls the table and the children can only add nullable fields to it. Those settings have to go somewhere and unless we have a very good reason to, I would not add the complexity of scraping and merging table settings from all children. What we can do here: * Improve the documentation if it hasn't been clear for you * Trigger a deprecation that we turn into an exception in 3.0 if `Table`, `UniqueConstraint` and friends are used on children in a single-table inheritance.
Author
Owner

@simPod commented on GitHub (Aug 10, 2023):

Hmm, since ORM should abstract me from looking at things from db perspective as much as possible, I did not want to think about the parent class as a table controller for all subclasses, but rather have relevant constraints etc. coupled with related subclasses. I can live with that of course.

However, UniqueConstraint with fields on parent class is ignored when it contains fields that are not present on the parent class but child. That goes a bit against

the root entity controls the table

You have to use UniqueConstraint with columns in order for it to work now.

@simPod commented on GitHub (Aug 10, 2023): Hmm, since ORM should abstract me from looking at things from db perspective as much as possible, I did not want to think about the parent class as a table controller for all subclasses, but rather have relevant constraints etc. coupled with related subclasses. I can live with that of course. However, `UniqueConstraint` with `fields` on parent class is ignored when it contains fields that are not present on the parent class but child. That goes a bit against > the root entity controls the table You have to use `UniqueConstraint` with `columns` in order for it to work now.
Author
Owner

@derrabus commented on GitHub (Aug 13, 2023):

ORM should abstract me from looking at things from db perspective as much as possible

Well yes, but the metadata mapping is the way you tell the ORM how it should do that. And annotations like UniqueConstraint simply don't make sense in a context where the database has been abstracted away completely.

However, UniqueConstraint with fields on parent class is ignored when it contains fields that are not present on the parent class but child. […] You have to use UniqueConstraint with columns in order for it to work now.

Let's fix that?

@derrabus commented on GitHub (Aug 13, 2023): > ORM should abstract me from looking at things from db perspective as much as possible Well yes, but the metadata mapping is the way you tell the ORM how it should do that. And annotations like `UniqueConstraint` simply don't make sense in a context where the database has been abstracted away completely. > However, `UniqueConstraint` with `fields` on parent class is ignored when it contains fields that are not present on the parent class but child. […] You have to use `UniqueConstraint` with `columns` in order for it to work now. Let's fix that?
Author
Owner

@Pixelshaped commented on GitHub (Oct 25, 2023):

I can confirm that when UniqueConstraint is declared on a parent class (seemingly the one that also declares the doctrine inheritance), then fields does not work, only columns.

@Pixelshaped commented on GitHub (Oct 25, 2023): I can confirm that when `UniqueConstraint` is declared on a parent class (seemingly the one that also declares the doctrine inheritance), then `fields` does not work, only `columns`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7213