Friday, 11 May 2012

How to configure the writable directories in Symfony2' on Mac OS

I know it's my set-up, so please no comments (not like anyone except me reads this stuff anyway lol) but I can't be arsed to change it because it works for everything else I do. But every time I create a new Symfony2 project and run it for the first time I get errors like

The directory "/path/to/my/workspace/MyProject/app/cache/dev/annotations" is not writable.

The answer's actually right there on the Symfony documentation, but if you can't be bothered to look for it (like me) or you somehow stumbled across this page first (doubt it) then the answer is as follows: Enter the follow commands but DON'T copy and paste... (see below)

rm -rf app/cache/*
rm -rf app/logs/*

sudo chmod +a "<name_of_your_http-server_user> allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs
sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs

Replace "<name of your http-server user>" with the actual name of the user that runs the httpd server on your system.

And if you get stuck...

Assuming you've got the error message above, or similar at least, enter:

ls -al /app/cache/
And you'll be able to work out from there what user you should be entering above. If you can't... you're reading the wrong blog.

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

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.