SUBSCRIBE VIA RSS


Subscribe to our feed

Symfony Experts

Symfony Experts
If you have an urgent question for a symfony-related issue, this is the place to ask.

Topics

Stack Overflow


The old fashioned way

RECENT TUNES

July 16, 2009 – 1:38pm PHP Doctrine: Fetching related objects with hasOne relationship or one-to-zero-or-one

People new to the Doctrine ORM have a hard time understanding the magic that happens behind the scenes when you want to fetch a related object through a has-one-or-zero relationship. Looking through the doctrine-user group, it’s clear there is a lot confusion on this to newcomers, especially if you are coming over from Propel or other ORMs. It can be a pretty big conceptual change to how things are done.

To use an example off of the doctrine-user group, imagine you have a Content object and this may or may not have a related Metadata record.

If you are familiar with Propel, then if you had your hydrated $content object, you could do

// with propel:
if ($metadata = $content->getMetadata()) {
  // do something with the related $metadata
}

and getMetadata() would return NULL if there was no related record to be found.

Doctrine will automatically create an empty related object if you request one, so calling $content->Metadata or $content['Metadata'] or $content->getMetadata() will always return a Metadata object, even if it does not exist in the database. So, you can’t rely on the same trick above to see if a related object exists.

So, how do you check if a related object actually exists? The first way, and my favorite, is using the exists() method to check if the related object is persistent (i.e. the object is saved in the DB).

// with doctrine 
if ($content->Metadata->exists()) { 
  // related metadata exists for this $content object
}

Using isset() will check if the related object has been hydrated/ loaded, but it will not do a query against the database to see if it exists there. So, if you fetch a Content object and do not do any joins in your query to join the related Metadata objects, then isset(), either by doing isset($business->Metadata) or $business->isset('Metadata') will *always* return false.

If your foreign key to the related object exists in your parent record (if content has a metadata_id field) you can always check the value of $content['metadata_id']. This will not work if the foreign key exists only in your metadata table (metadata has a column content_id, but content does not have a metadata_id column).

I think if you really wanted to do it right, if you expect to be checking for the existence of a related object (“Metadata”) on your collection of core objects (“Content”), you should do a join in your query so you don’t have to hit the database for every single Content record fetched to see if a related Metadata record exists.

Here are a few threads on this subject in the doctrine-user group:

Posted by in  Web Development   |  

3 Responses to PHP Doctrine: Fetching related objects with hasOne relationship or one-to-zero-or-one

  1. Rich Sage says:

    Great post – I spent 2 days last week discovering the hard way how this works, having come from a Propel background to Doctrine… and then came across your post today typically! Cheers 🙂

  2. Scott Meves says:

    More great information here:

    If you ever end up with empty related records, the culprit might be these auto-generated objects that get created with you call certain magic methods on an object.

    
    if ($user->Email->exists()) {
        // User has e-mail
    } else {
        // User does not have a e-mail
    }
    
    $user->clearRelated('Email');
    

    “Because Doctrine will automatically create a new Email object if the user does not have one, we need to clear that reference so that if we were to call $user->save() it wouldn’t save a blank Email record for the User.” – http://www.doctrine-project.org/documentation/manual/1_2/en/working-with-models

    A better method might be:

    
    if ($user->relatedExists('Email')) {
    // now we won't get an empty Email record when we save $user
    }
    
  3. Epoc says:

    Thanks, I was really stuck for days !