Sunday 6 May 2012

Mapped properties in Symfony2 and Doctrine2 Entity inheritance

I discovered an interesting "feature" in the use of Doctrine's annotations today which caused me a little hunting around and trial and error to fix. But I'm posting about this here because a) as usual I'm useless at remembering stuff but more importantly b) I couldn't find any reference to this in the documentation and a lot of google searches didn't turn up anything relevant

The set-up

I have what I consider to be a fairly straight forward object graph:

  • Abstract BaseEntity
  • Extended by abstract BaseAuthUser
  • Extended by bundle specific User class

A (simplified) example of these classes are below:

/**
 * BaseEntity class
 * @ORM\MappedSuperclass
 */
abstract class BaseEntity {

    /**
     * @ORM\Column(type="bigint")
     * @ORM\Id
     * @ORM\GenerationStrategy(strategy="AUTO")
    private $id;

    // getters and setters ..l
}

/**
 * BaseAuthUser class
 * @ORM\MappedSuperclass
 */
abstract class BaseAuthUser extends BaseEntity {
    /**
  * @ORM\Column(type="string", length=100, unique=true)
  * @var string
  */
 private $email;
 /**
  * @ORM\Column(type="string", length=20, unique=true)
  * @var string
  */
 private $username;
 /**
  * @ORM\Column(type="string", length=20)
  * @var string
  */
 private $password;
 /**
  * @ORM\Column(type="string", length=32)
  * @var string
  */
 private $salt;

 /**
  * @ORM\Column(type="boolean", name="is_active")
  * @var boolean
  */
 private $isActive;
 
 // getters and setters ...
}

/**
 * MyBundleUser class
 * @ORM\Entity
 * @ORM\Table(name="auth_user")
 */
class MyBundleUser extends BaseAuthUser {

    // no specific properties mapped to database columns..
}

By annotating my classes this way, my understanding was that when I ran the command

php app/console doctrine:schema:create
I would have a single table called 'auth_user' built with the columns as defined in the BaseEntity and BaseAuthUser classes. And this was exactly what happened. Cool... one step closer

The next step was to ensure I have all the correct getters and setters and Doctrine has mapped my entity's metadata by running

php app/console doctrine:generate:entities MyBundleUser
which appeared to go well. As a result, I subsequently built the basic CRUD actions and templates by calling
php app/console doctrine:generate:crud

So at this point I have a working controller, and a form where I can add a new user. Or so I thought. When I actually filled out the form and hit submit, I got an error saying that my required email property was null. Um... well, that can't be right, I entered an email value. What's going on?

By running through debug, I could see that all the correct values were being received by the form, and my entity was being populated with the same values, but the SQL query was entering nulls. It turned out that the the

doctrine:generate:entities
command had added the private members and the associated getters and setters to my MyBundleUser class even though they were already defined in the BaseAuthUser class. So (my guess is) the form code was setting those values to my MyBundleUser class, but the Doctrine persistence code was looking at the getters and setters from the BaseEntity (somehow)...

Anyway - the solution was actually pretty simple but again, if the information is there in the documentation, I couldn't find it (if you know where it is, perhaps you'd like to leave a link in the comments...?). What's required, simply specify the name option in the @ORM\Column definitions and the property is no longer replicated in the sub-class. Once I had updated all of my classes to specify the name option, my object is persisted correctly.

No comments:

Post a Comment

Please leave your feedback and comments. I love to discuss this stuff!