Share » Learn » eZ Publish » The PersistentObject eZ Component:...

The PersistentObject eZ Component: Putting Relations Where Relations Belong

Tuesday 27 February 2007 12:12:00 am

  • Currently 3 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

We just defined our first relation: the one-to-one relation between the person and detail objects. Additionally, we saw that in most one-to-one cases the same keys are used for related objects. As a result, one of them needs to use the manual generator. We also saw how to save newly created persistent objects and how to relate two persistent objects to each other.

Next, we will define some more relation types and see how to fetch objects that are related to each other.

In our example, the show action is responsible for displaying a person object, its details, its email addresses and its physical addresses. The actions template also shows a list of all addresses so that the user can assign new ones to a person. Therefore, we need to fetch all address objects as well.

<?php
// ...
 
$session = ezcPersistentSessionInstance::get();
$this->person       = $session->load( "ezcappContactPerson", $this->id );
$this->detail       = $session->getRelatedObject( $this->person, "ezcappContactDetail" );
$this->emails       = $session->getRelatedObjects( $this->person, "ezcappContactEmail" );
$this->addresses    = $session->getRelatedObjects( $this->person, "ezcappContactAddress" );
 
$addressQuery = $session->createFindQuery( "ezcappContactAddress" );
$addressQuery->orderBy( "Zip" )->orderBy( "City" )->orderBy( "Street" );
$this->allAddresses = $session->find( $addressQuery, "ezcappContactAddress" );
 
// ...
?>

As usual, we need an instance of ezcPersistentSession for any kind of action on a persistent object. Next, we fetch the desired person from the database using $session->load(). If you are using this method, you should be aware that it will throw an exception if the desired object does not exist. To fetch an object only if it exists, use the method loadIfExists().

Then, we need to fetch the three related objects: the detail object, the email objects and the address objects. Note that two different methods are used for this: getRelatedObject() (used for fetching the detail object) will fetch exactly one object and will throw an exception if no object is found. In contrast, getRelatedObjects() will fetch all related objects from the database (0 or more) and will always return an array of objects (even if it is empty), no matter how many were found.

Finally, we need the list of all address objects. This has actually nothing to do with the topic of relations. The ezcPersistentSession instance can create a query object for us, which is already prepared to find objects of a given class ($session->createFindQuery()). The query object we retrieve is an instance of ezcQuerySelect, which is part of the SQL abstraction system of the Database component. Using this object as the basis, we can restrict the performed search and add SQL parts to it. As you can see, the query class uses a so-called "fluent interface", where each method call returns the object itself, so that one can call methods in a chain. We are adding three ORDER BY clauses to our query here. Finally, we instruct the persistent session to find all objects of the class ezcappContactAddress that match the given query and return an array of them.

Let us now take a look at the definitions for the relations we just used. Again, we are enhancing the definition file of the person object:

<?php
// ...
 
// 1:n relation to email table
$def->relations['ezcappContactEmail'] = new ezcPersistentOneToManyRelation(
    "person",
    "email"
);
$def->relations['ezcappContactEmail']->columnMap = array(
    new ezcPersistentSingleTableMap(
        "id",
        "person"
    ),
);
$def->relations['ezcappContactEmail']->cascade = true;
 
 
// n:m relation to address table
$def->relations['ezcappContactAddress'] = new ezcPersistentManyToManyRelation(
    "person",
    "address",
    "person_address"
);
$def->relations['ezcappContactAddress']->columnMap = array(
    new ezcPersistentDoubleTableMap(
        "id",
        "person",
        "address",
        "id"
    ),
);
 
// ...
?>

The relation between the person and email objects is the most common relation type, the one-to-many relation. It is defined completely analagous to the one-to-one relation, using the specific relation class and the two tables we want to connect to each other. Since we are only using two tables, we need a single table map, just as for ezcPersistentOneToOneRelation. The cascade option again has the same effect: if a person is deleted, all of the associated email addresses are deleted too.

The second relation we define here is the most complex type: the many-to-many relation. It is more complex than the other two types, because we need a relation table to implement it. Therefore, the constructor of ezcPersistentManyToManyRelation requires three table names instead of two: the source table, the destination table and the relation table.

We are connecting the person table to the address table using the person_address table. Therefore, the column map of a n:m relation is an array of ezcPersistentDoubleTableMap, which connects the three named tables to two table connections. First, the field from the source table maps to the first field of the relation table. The third item is the second field of the relation table, which maps to the field from the destination table. In other words, we are mapping the column id of the table person to the column person of the table person_address and the column address of the table person_address to the column id of the table address.

More about relations

As already mentioned, the many-to-many relation is the most complex relation type. Earlier, we loaded all addresses so that the user can assign them to a person. To analyze this a bit deeper, we will take a look at the address action:

<?php
// ...
 
$session = ezcPersistentSessionInstance::get();
$this->person  = $session->load( "ezcappContactPerson", $form->person );
$this->address = $session->load( "ezcappContactAddress", $form->address );
// ...
$session->addRelatedObject( $this->person, $this->address );
 
// ...
?>

We load both desired objects from the database, using the well-known load() method. Again, we are using the addRelatedObject() method, but this time without saving any of the objects afterwards. For all other relation types, the addRelatedObject() method simply sets the desired object properties to the correct values. This manipulates the objects themselves, so we need to store them. In the case of a many-to-many relation, the original objects are not modified in any way. Instead, a new record is inserted into the relation table, which occurs without any manual saving.

So, let us also examine the counterpart: the removeaddress action.

<?php
// ...
 
$session = ezcPersistentSessionInstance::get();
$this->person  = $session->load( "ezcappContactPerson", $form->person );
$this->address = $session->load( "ezcappContactAddress", $form->address );
// ...
$session->removeRelatedObject( $this->person, $this->address );
 
// ...
?>

The same procedure occurs here: both objects are loaded, then a call to removeRelatedObject() removes the relation. As you might guess, you can also use this method with all other types of relations, where it will simply unset the relation properties to remove the relation. If desired, you would have to manually remove the objects afterwards.

As a final word about many-to-many relations, notice that the cascade option does not exist for them. PersistentObject does not know (without more SQL query overhead) if related objects can be deleted, as it needs to find out whether they are referenced by other objects.

The final relation type is the many-to-one relation. With all of the relations we configured so far, we can fetch objects from the database that are related to a person object. What happens if we want to fetch a person object related to one of those other objects? We need to configure the corresponding relation.

If you want to learn more about this relation type (or so-called "reverse" relations), you should take a look at the definition files for the email and address objects. The PersistentObject tutorial also contains some information about this.

36 542 Users on board!

Tutorial menu

Printable

Printer Friendly version of the full article on one page with plain styles

Author(s)