Sander Marechal
Sander Marechal

Reputation: 23216

Doctrine table class inheritance when one subclass has no extra attributes

I'm having a problem with my mapping. I can't get it to work. I have an abstract base class like so:

/**
 * @Entity
 * @Table(name="actions")
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"FOO" = "FooAction", "BAR" = "BarAction", ...})
 */
abstract class AbstractAction
{
    ...
}

I have a bunch of different actions, all with different fields. E.g:

/**
 * @Entity
 * @Table(name="actions_foo")
 */
class FooAction extends AbstractAction
{
   ...
}

But one of my actions (BarAction) does not need any extra fields besides those supplied by the AbstractAction. But how can I map that? I tried omitting the @Table, or using the same @Table as the AbstractAction, but to no effect.

/**
 * @Entity
 * @Table(name="actions")
 */
class BarAction extends AbstractAction
{
   ...
}

Omitting the @Table gives me a PDOException about a missing table BarAction. Using the @Table of the base class gives me:

PDOException: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

So, how do I map this?

Edit: So far I have tried two more things.

I tried removing the @Entity as well as the @Table from BarAction in the hope that this way it would no longer require a database table. That doesn't work. Instead, I get this error:

Doctrine\ORM\Mapping\MappingException: Class BarAction is not a valid entity or mapped super class.

Next I tried creating an actions_bar table in my database with just a single foreign key column id. Then I mapped the BarAction to it. That works (yay!) but it feels crufty and ugly to have an extra SQL table that I don't need at all.

So, still looking for a better way...

Upvotes: 16

Views: 5797

Answers (5)

Jan_
Jan_

Reputation: 46

Bit of a late response but it may be useful for others asking this question.

Make the class 'AbstractAction' concrete and add a mapping for it in the discriminator map (You probably want to rename it at this point)

 * @DiscriminatorMap({"ABSTRACT" = "AbstractAction", "FOO" = "FooAction", "BAR" = "BarAction", ...})
 */
class AbstractAction
{

You should then use that table for rows which need no additional columns

Upvotes: 0

Broncha
Broncha

Reputation: 3794

I also ran into this problem, but I just added some dummy properties into my inherited entity, thinking it can be useful any time. I know its not the perfect idea, but it helped me keep my data model intact and give me the possibility to have own properties in the inherited entities in the future.

Upvotes: 0

Hakan Deryal
Hakan Deryal

Reputation: 2903

You are using joined inheritance model (class table inheritance), which uses a separate table for parent and each child. If you don't specify any fields in the child class, Doctrine will just create a table only containing ID field.

And a parent class can only use one type of inheritance, either class table inheritance or single table inheritance.

In that case, if you don't want to have a table with only id column in it, you need to change your data model.

Upvotes: 2

Guillaume Poussel
Guillaume Poussel

Reputation: 9822

I think you do not have to create manually the table in your database.

I have pretty much the same structure an I let Doctrine (2.3) do the job. You have to put @Entity et @Table on each subclasses though. The error Invalid parameter number: number of bound variables does not match number of tokens may not be related. It could be a cache problem, have you tried flushing it?

Upvotes: 0

acanimal
acanimal

Reputation: 5020

Maybe this can help or add something new. It is not a how to answer but simply taking about concepts.

If you think on classes and you understand your model as: AbstractAction, FooAction and BarAction it is probably because you can have the same methods implemented in a different way on the subclasses or extending some parent method.

If you choose to represent these classes with tables and you choose the "all in one table with a discriminator attribute" i think you have no problem. For the BarAction you will have register with discriminator="BarAction" which will be reprsented by a BarAction.php entity class.

On the other hand if you opt for using different table I consider you need one table per "class". The table for BarAction will only containd the ID field for the AbstractAction but its is required to "classify" (or discriminate) your data as a FooAction.

In summary, I think three tables representing three classes are a fine solution, although the BarAction table simply contains a "link" to the parent table.

Upvotes: 2

Related Questions