Saturday, July 6, 2013

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:



- Full Post

No comments:

Post a Comment