Doctrine 2: Give me my constructor back

Tags: doctrine2

Posted about 1 year ago by jwage

At ConFoo 2010 during my presentation, someone asked about the constructor of entities in Doctrine 2 and whether or not it could be used. I think this is something worth writing about since in Doctrine 1 this was not possible. The constructor was hi-jacked from you and used internally by Doctrine.

In Doctrine 2 it is possible to define the constructor in your entity classes and is not required to be a zero argument constructor! That's right, Doctrine 2 never instantiates the constructor of your entities so you have complete control!

This is possible due to a small trick which is used by two other projects, php-object-freezer and Flow3. The gist of it is we store a prototype class instance that is unserialized from a hand crafted serialized string where the class name is concatenated into the string. The result when we unserialize the string is an instance of the class which is stored as a prototype and cloned everytime we need a new instance during hydration.

Have a look at the method responsible for this:

<?php

public function newInstance()
{
    if ($this->_prototype === null) {
        $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
    }
    return clone $this->_prototype;
}

The above code allows us to have entities where we make full use of our constructor like the following class definition:

<?php

namespace Entities;

/** @Entity */
class User
{
    /** @Column */
    private $username;

    /** @Column */
    private $password;

    public function __construct($username, $password)
    {
        $this->username = $username;
        $this->password = md5($password);
    }
}

Now of course you can use the constructor like you would expect:

<?php

use Entities\User;

$user = new User('jwage', 'changeme');

Conclusion

It is an interesting solution and did not have much if any effect on hydration performance. At any rate the cost is well worth it to get complete control of your entities. No longer are the days where Doctrine greedily steals your precious class constructor!


Comments (5) [ add comment ]

That's just great. Posted by David about about 1 year ago.

Just great. A crazy hack, but still great! ;-)

. Posted by romanb about about 1 year ago.

It is worth mentioning that even though this implementation "hijacks" __wakeup/__clone, it is still relatively easy to use them safely:

http://www.doctrine-project.org/documentation/cookbook/2_0/en/implementing-wakeup-or-clone

Note also that this implementation does not affect implementors of the Serializable interface.

Is this still true for alpha-4? Posted by disago about about 1 year ago.

When doctrine 2 alpha-4 reconstructs entities with constructors PHP Warning "Missing argument X ..." follows pointing at line 1742 of UnitOfWork that is:

$entity = new $className;

I don't see how this implements the trick described in the post that allows to use parameters in constructors.

Am I doing something wrong?

BTW.. Congrats to the people working hard on this excellent, DDD-friendly ORM. At last!!! Thnkx!!!

@disago Posted by romanb about about 1 year ago.

You're right, this feature is not contained in ALPHA4, it was added afterwards. Thus you either need to use trunk or wait for the first beta release which will be in the coming weeks.

Yep, its working now Posted by disago about about 1 year ago.

@romanb: Thanks, that did the trick. I'm using Doctrine 2 for a project to be delivered in about 2 months. I'll use basic ORM mapping (no inheritance, etc) and think it will be safe enough then to be used in controlled production (very few users). What do you think?

Create Comment