Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-511

Schema tool does not support STI - attempts to create a table per subclass

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0-BETA1
    • Fix Version/s: 2.0-RC1
    • Component/s: Tools
    • Security Level: All
    • Labels:
      None

      Description

      A recent update broke all support for STI.

      Now the following does not work:

      /**
       * @MappedSuperclass
       * @InheritanceType("SINGLE_TABLE")
       * @DiscriminatorColumn(name="type", type="string")
       * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
       * @Table(name="user")
       */
      abstract class Principal
      {}
      /**
       * @Entity
       */
      class Group extends Principal
      {}
      /**
       * @Entity
       */
      class User extends Principal
      {}
      

      Attempting to use orm:schema-tool:create results in the following error:

      SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Group (user_id INT NOT NULL, username VARCHAR(45) NOT NULL, password VARCHAR(40)' at line 1

      The SQL generated is:

      CREATE TABLE Group (user_id INT NOT NULL, username VARCHAR(45) NOT NULL, password VARCHAR(40) NOT NULL, status SMALLINT NOT NULL, firstname VARCHAR(45) NOT NULL, lastname VARCHAR(45) NOT NULL, email VARCHAR(60) NOT NULL, phone VARCHAR(20) DEFAULT NULL, pager VARCHAR(20) DEFAULT NULL, reset_code VARCHAR(6) DEFAULT NULL, is_password_expired TINYINT(1) NOT NULL, failed_login_count SMALLINT NOT NULL, is_admin TINYINT(1) NOT NULL, version INT DEFAULT 1 NOT NULL, UNIQUE INDEX Group_username_uniq (username), PRIMARY KEY(user_id)) ENGINE = InnoDB;

      CREATE TABLE User (user_id INT NOT NULL, username VARCHAR(45) NOT NULL, password VARCHAR(40) NOT NULL, status SMALLINT NOT NULL, firstname VARCHAR(45) NOT NULL, lastname VARCHAR(45) NOT NULL, email VARCHAR(60) NOT NULL, phone VARCHAR(20) DEFAULT NULL, pager VARCHAR(20) DEFAULT NULL, reset_code VARCHAR(6) DEFAULT NULL, is_password_expired TINYINT(1) NOT NULL, failed_login_count SMALLINT NOT NULL, is_admin TINYINT(1) NOT NULL, version INT DEFAULT 1 NOT NULL, UNIQUE INDEX User_username_uniq (username), PRIMARY KEY(user_id)) ENGINE = InnoDB;

        Issue Links

          Activity

          Hide
          Guilherme Blanco added a comment - - edited

          That seems to be a reserved keyword issues.
          I see comments around the STI issues, but this one doesn't seem to be the case.

          Try to use a different table name:

          @Table(name="groups")
          

          and...

          @Table(name="users")
          

          This should fix the issue.

          Cheers,

          Show
          Guilherme Blanco added a comment - - edited That seems to be a reserved keyword issues. I see comments around the STI issues, but this one doesn't seem to be the case. Try to use a different table name: @Table(name= "groups" ) and... @Table(name= "users" ) This should fix the issue. Cheers,
          Hide
          David Abdemoulaie added a comment -

          No. This is a STI problem. I don't want two different tables. Users and Groups are meant to share the same table "user". This worked prior to the omega CLI changes.

          Show
          David Abdemoulaie added a comment - No. This is a STI problem. I don't want two different tables. Users and Groups are meant to share the same table "user". This worked prior to the omega CLI changes.
          Hide
          Roman S. Borschel added a comment -

          After thinking about this a while, I think it is an interesting problem that points out some conceptual issues as well as some opportunities. The reason SchemaTool behaves that way is simple, I think: A mapped superclass is nothing more than a regular php class that inherits its mapping information to the child classes "as if it were defined directly on the child classes". It is mainly a way to "reuse" mapping information without copy/paste. So the example given in the issue description is pretty much the same as this:

          abstract class Principal
          {}
          /**
           * @Entity
           * @InheritanceType("SINGLE_TABLE")
           * @DiscriminatorColumn(name="type", type="string")
           * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
           * @Table(name="user")
           */
          class Group extends Principal
          {}
          /**
           * @Entity
           * @InheritanceType("SINGLE_TABLE")
           * @DiscriminatorColumn(name="type", type="string")
           * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
           * @Table(name="user")
           */
           */
          class User extends Principal
          {}
          

          Hence why it wants to create two tables. However, something else is also probably wrong because it should rather result in an error due to creating the same table twice ("user"). So deriving @Table from a mapped superclass seems to be uncharted territory also. How/Why this ever worked before as you say is still a mystery to me. Do you know by any chance a revision or tag (alpha4?) where this worked so that we can compare to that?

          As you already found out, making the parent class an @Entity should work fine. We need to think more about how to deal with inheritance mapping information derived from mapped superclasses. For example, what should happen if we take the original example in the issue description but use JOINED instead like this:

          /**
           * @MappedSuperclass
           * @InheritanceType("JOINED")
           * @DiscriminatorColumn(name="type", type="string")
           * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
           */
          abstract class Principal
          {}
          /**
           * @Entity
           */
          class Group extends Principal
          {}
          /**
           * @Entity
           */
          class User extends Principal
          {}
          

          Can this ever work or does it even make sense? Rewriting it to reflect what it actually "looks like" for Doctrine:

          abstract class Principal
          {}
          /**
           * @Entity
           * @InheritanceType("JOINED")
           * @DiscriminatorColumn(name="type", type="string")
           * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
           */
          class Group extends Principal
          {}
          /**
           * @Entity
           * @InheritanceType("JOINED")
           * @DiscriminatorColumn(name="type", type="string")
           * @DiscriminatorMap({"User" = "User", "Group" = "Group"})
           */
          class User extends Principal
          {}
          

          I think this would make no sense at all. Hence the combination of @MappedSuperclass + @InheritanceType probably only seems to make sense currently in the SINGLE_TABLE scenario mentioned in the issue description.

          An interesting possibility I noticed due to this ticket is that you can indeed use different inheritance mapping strategies in the same hierarchy, as long as the different subtrees of the hierarchy that are mapped with different strategies do not have a common ancestor entity. (For Doctrine the subtrees are then simply unrelated hierarchies, it does not care about common ancestor classes that are regular php classes). We should write some tests for that and add it to the docs, I will create a new ticket for that. Example:

          class Base {}
          class RootOfTreeA extends Base {} // Entity + SINGLE_TABLE
          class RootOfTreeB extends Base {} // Entity + JOINED
          
          Show
          Roman S. Borschel added a comment - After thinking about this a while, I think it is an interesting problem that points out some conceptual issues as well as some opportunities. The reason SchemaTool behaves that way is simple, I think: A mapped superclass is nothing more than a regular php class that inherits its mapping information to the child classes "as if it were defined directly on the child classes". It is mainly a way to "reuse" mapping information without copy/paste. So the example given in the issue description is pretty much the same as this: abstract class Principal {} /** * @Entity * @InheritanceType( "SINGLE_TABLE" ) * @DiscriminatorColumn(name= "type" , type= "string" ) * @DiscriminatorMap({ "User" = "User" , "Group" = "Group" }) * @Table(name= "user" ) */ class Group extends Principal {} /** * @Entity * @InheritanceType( "SINGLE_TABLE" ) * @DiscriminatorColumn(name= "type" , type= "string" ) * @DiscriminatorMap({ "User" = "User" , "Group" = "Group" }) * @Table(name= "user" ) */ */ class User extends Principal {} Hence why it wants to create two tables. However, something else is also probably wrong because it should rather result in an error due to creating the same table twice ("user"). So deriving @Table from a mapped superclass seems to be uncharted territory also. How/Why this ever worked before as you say is still a mystery to me. Do you know by any chance a revision or tag (alpha4?) where this worked so that we can compare to that? As you already found out, making the parent class an @Entity should work fine. We need to think more about how to deal with inheritance mapping information derived from mapped superclasses. For example, what should happen if we take the original example in the issue description but use JOINED instead like this: /** * @MappedSuperclass * @InheritanceType( "JOINED" ) * @DiscriminatorColumn(name= "type" , type= "string" ) * @DiscriminatorMap({ "User" = "User" , "Group" = "Group" }) */ abstract class Principal {} /** * @Entity */ class Group extends Principal {} /** * @Entity */ class User extends Principal {} Can this ever work or does it even make sense? Rewriting it to reflect what it actually "looks like" for Doctrine: abstract class Principal {} /** * @Entity * @InheritanceType( "JOINED" ) * @DiscriminatorColumn(name= "type" , type= "string" ) * @DiscriminatorMap({ "User" = "User" , "Group" = "Group" }) */ class Group extends Principal {} /** * @Entity * @InheritanceType( "JOINED" ) * @DiscriminatorColumn(name= "type" , type= "string" ) * @DiscriminatorMap({ "User" = "User" , "Group" = "Group" }) */ class User extends Principal {} I think this would make no sense at all. Hence the combination of @MappedSuperclass + @InheritanceType probably only seems to make sense currently in the SINGLE_TABLE scenario mentioned in the issue description. An interesting possibility I noticed due to this ticket is that you can indeed use different inheritance mapping strategies in the same hierarchy, as long as the different subtrees of the hierarchy that are mapped with different strategies do not have a common ancestor entity . (For Doctrine the subtrees are then simply unrelated hierarchies, it does not care about common ancestor classes that are regular php classes). We should write some tests for that and add it to the docs, I will create a new ticket for that. Example: class Base {} class RootOfTreeA extends Base {} // Entity + SINGLE_TABLE class RootOfTreeB extends Base {} // Entity + JOINED
          Hide
          Roman S. Borschel added a comment -

          A solution and/or documented behavior for this should be evaluated before entering RC1.

          Show
          Roman S. Borschel added a comment - A solution and/or documented behavior for this should be evaluated before entering RC1.
          Hide
          Benjamin Eberlei added a comment -

          MappedSuperclass Inheritance Details are confusing and are ignored now.

          Show
          Benjamin Eberlei added a comment - MappedSuperclass Inheritance Details are confusing and are ignored now.

            People

            • Assignee:
              Benjamin Eberlei
              Reporter:
              David Abdemoulaie
            • Votes:
              1 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: