++ Dealing with relations
++ Many-to-Many relations
+++ Creating a new link
Lets say we have two classes User and Group which are linked trhough a GroupUser association class. When working with transient (new) records the fastest way for adding a User and couple of Groups for it is:
$user = new User();
$user->name = 'Some User';
$user->Group[0]->name = 'Some Group';
$user->Group[1]->name = 'Some Other Group';
$user->save();
However in real world scenarious you often already have existing groups, where you want to add a given user. The most efficient way of doing this is:
$gu = new GroupUser();
$gu->user_id = $userId;
$gu->group_id = $groupId;
$gu->save();
+++ Deleting a link
While the obvious and convinient way of deleting a link between User and Group would be the following, you still should *NOT* do this:
$user = $conn->getTable('User')->find(5);
$user->GroupUser
->remove(0)
->remove(1);
$user->save();
This is due to a fact that $user->GroupUser loads all group links for given user. This can time-consuming task if user belongs to many groups. Even if the user belongs to few groups this will still execute an unnecessary SELECT statement.
The right way to delete links between many-to-many associated records is by using the DQL DELETE statement. Convenient and recommended way of using DQL DELETE is trhough the Query API.
$deleted = Doctrine_Query::create()
->delete()
->from('GroupUser')
->addWhere('user_id = 5')
->whereIn('group_id', $groupIds);
->execute();
// print out the deleted links
print $deleted;
++ Component overview
++ Fetching objects
+++ Field lazy-loading
Whenever you fetch an object that has not all of its fields loaded from database then the state of this object is called proxy. Proxy objects can load the unloaded fields lazily.
Lets say we have a User class with the following definition:
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 20);
$this->hasColumn('password', 'string', 16);
$this->hasColumn('description', 'string');
}
}
In the following example we fetch all the Users with the fields name and password loaded directly. Then we lazy-load a huge field called description for one user.
$users = Doctrine_Query::create()->select('u.name, u.password')->from('User u');
// the following lazy-loads the description fields and executes one additional database query
$users[0]->description;
Doctrine does the proxy evaluation based on loaded field count. It does not evaluate which fields are loaded on field-by-field basis. The reason for this is simple: performance. Field lazy-loading is very rarely needed in PHP world, hence introducing some kind of variable to check which fields are loaded would introduce unnecessary overhead to basic fetching.