Doctrine Lazy Loading

Posted on May 29, 2009 by jwage


Did you know?

Did you know that in Doctrine if you don't specifically select a property or join a relationship in your DQL query, you can lazily load that data? Here is a simple example to demonstrate.

<?php
$q = Doctrine::getTable('User')
  ->createQuery('u')
  ->select('u.id, u.username')
  ->where('u.username = ?', 'jwage');

$user = $q->fetchOne();

Notice how I only selected the id and username, but we have an additional column named email_address. If I were to access that property it would lazily load the data.

<?php
echo $user['email_address']

**NOTE** The above code would result in an additional query to the
database that would lazy load all unloaded properties of an object
in proxy state. A record is in proxy state when it has one or more
properties that have not been loaded from the database.

Be aware

This can be both a curse and a blessing because if you lazily load many properties and relationships this means you will have a high query count per page. In Doctrine 1.x lazy loading is on by default so it is often overly used. The problem is people aren't even aware that it is happening.

More Examples

Imagine you had a User and Profile model where User has one Profile and Profile has one User, a simple one-to-one relationship. If I were to write a DQL query where I retrieve an individual user record like the following.

<?php
$q = Doctrine::getTable('User')
  ->createQuery('u')
  ->where('u.username = ?', 'jwage');

$user = $q->fetchOne();

But then I wanted to retrieve the Profile for that User I could do so by simply accessing the Profile property on User.

<?php
$profile = $user->Profile;

The above code would result in an additional query to the database that loads the Profile record for the User in question. Now if I were to be rendering a list of twenty User objects and I lazily load the profile of each user that means I would have 21 queries total! That is too many! In that case it would be smart to leftJoin() the Profile object like the following.

<?php
$q = Doctrine::getTable('User')
  ->createQuery('u')
  ->leftJoin('u.Profile p');

$users = $q->execute();

Now when I access the Profile relationship of an individual object the data would already be present and no lazy loading would be required.

Why?

I made this blog post because I noticed this feature being abused and overly used just because it makes things easier. The goal is to make you aware of what is happening so that you can effectively use lazy loading and not end up having applications with very high query counts per page.

I've seen this many times and the blame is often put on Doctrine but really it is the tool not being used properly. So, be aware of when you are lazy loading properties and the total number of queries you execute per page. Make sure you always join required relationships and only select the properties you need to access. It doesn't make sense to load data if you're not going to be using it.

How?

If you need help with keeping track of how many queries you have per page, frameworks like Symfony and Zend Framework give you debug tools to show you how many queries you have per page. Or of course you can always use the Profiling tool built in to Doctrine to log the queries in your application and keep track of it that way.