Moving from Propel to Doctrine

Oh I like the Unit of Work pattern! I’m used to using Unit of Work and Repositories (we call them Finders) in the Java world of my professional career. I was sceptical at first because of the increased code complexity, but when the platform provides it it is nice. At work we have worked hard to optimise it, for example using JDBC Batch mode.

Back in the PHP world a task on my web site is logging in a Facebook user. That user needs an entry in the site database to which all other tables join. Once logged in the user becomes a normal user. The user name is read from Facebook, and may have changed.

The Propel solution had the following code in the Login.php class

private static function getOrCreateFacebookUser($facebookId, $facebookName)
{
	$user = UserQuery::create()->filterByFacebookId($facebookId)->findOne();

	if(!$user)
	{
		$user = new User();
		$user->setFacebookId($facebookId);	
		$user->setName($facebookName);
		$user->createNewPostingKey();
		$user->save();
	}
        else if($user->getName() != $facebookName)
	{
		$user->setName($facebookName);
		$user->save();
	}
	return $user;
}

In Doctrine I have placed this method in the User Repository. The caller is responsible for committing the Unit of Work using Doctrine’s flush() method. I can set the name property with impunity knowing that Doctrine will not write the change to the database if it does not need to.

        /**
         * Find or create a User for a given set of Facebook credentials.
         * The Entity Manager is not flushed.
         */
        public function findOrCreateFacebookUser($facebookId, $facebookName)
        {
                $user = $this->findOneBy(array('facebook' => $facebookId));
                if(! $user)
                {
                        $user = new User();
                        $user->setFacebook($facebookId);
                        $user->setName($facebookName);
                        $user->makeNewPostingKey();
                        $this->_em->persist($user);
                }
                $user->setName($facebookName);
                return $user;
        }

Doctrine provides an Entity Map, another advantage of Unit of Work. This would mean that if any other query within the Unit of Work looks up the user then they’ll find my already loaded and modified version. All we need is soft locking and Doctrine will be getting close to my work environment. The Doctrine manual notes that my newly created user will not show up in queries. Doctrine also provides quite granular control over the Unit of Work, which may help in some situations.

My test script for the above and related code is

$user = $entityManager->getRepository('\Entities\User')->findOrCreateFacebookUser(1000,'Test Record');
$user->setPassword("Top Secret Password");
if($user->checkPassword("Top Secret Password"))
{
        print("Check password OK\n");
}
if(!$user->checkPassword("The Wrong Password"))
{
        print("Check password incorrect OK\n");
}
$entityManager->flush();
print_r($user);

This works as expected. If I modify the created user record on the database and rerun I see the change in the print_r output. When I rerun the script I note that the new password hash has been saved to the database.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.