| eZ Components - PersistentObject |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| .. contents:: Table of Contents |
| |
| Introduction |
| ============ |
| The PersistentObject component provides object persistence for PHP 5 using a |
| database. PersistentObject uses the Database component to provide database |
| abstraction. It does not rely on code generation and does not force a specific |
| inheritance structure to work. |
| |
| PersistentObject is built to be fast and flexible, allowing you to build |
| persistent classes in the same way as any other class in your application. It |
| also utilizes the query abstraction layer of the Database component to make it |
| easy to build queries. Please refer to the API documentation of ezcQuerySelect |
| for detailed examples on how to use the query abstraction layer. |
| |
| The PersistentObjectDatabaseSchemaTiein component allows you to automatically |
| generate the definition files needed by PersistentObject from a database schema |
| or the structure of your database. For more information, please refer to |
| the API documentation on ezcPersistentObjectSchemaGenerator. |
| |
| Class overview |
| ============== |
| |
| ezcPersistentSession is the main API for interacting with the persistent |
| objects. Loading, saving and deleting persistent objects is done through this |
| class. ezcPersistentSessionIdentityDecorator can be used to add identity |
| mapping support to it (see `Identity mapping`_). |
| |
| Basic usage |
| =========== |
| |
| This chapter describes the typical usage of PersistentObject with a |
| single persistent class, using MySQL as the persistence storage. |
| |
| The persistent class |
| -------------------- |
| |
| We want to make a simple class, representing a person, persistent using |
| a persistent object. This is a simple class with only a few members:: |
| |
| <?php |
| class Person |
| { |
| private $id = null; |
| public $name = null; |
| public $age = null; |
| |
| public function getState() |
| { |
| $result = array(); |
| $result['id'] = $this->id; |
| $result['name'] = $this->name; |
| $result['age'] = $this->age; |
| return $result; |
| } |
| |
| public function setState( array $properties ) |
| { |
| foreach( $properties as $key => $value ) |
| { |
| $this->$key = $value; |
| } |
| } |
| } |
| ?> |
| |
| The id member will map to the required persistent identifier. It has to default |
| to null. This is not required for any of the other mapped members. The id field |
| is a required unique identifier for this persistent object. It is generated by |
| the identifier generator and usually maps to an auto increment column in the |
| database. |
| |
| For simplicity, we have made the name and age members of the Person class |
| public. However, this is not required and in a real application you can use |
| any access method you like. You can even make the data completely private. |
| |
| All persistent objects must implement the getState() and setState() |
| methods. They are used to retrieve the state of the object when saving it and |
| to set it when loading it. The getState() method should always return the |
| complete state of the object while the setState() method should set |
| only one member at the time. |
| |
| The state arrays used by getState() and setState() must be indexed by the |
| property names, as defined in the persistence mapping below. They do not act on |
| the database columns names. |
| |
| The persistence mapping |
| ----------------------- |
| |
| We are going to map the Person class onto the following SQL table:: |
| |
| CREATE TABLE persons |
| ( |
| id integer unsigned not null auto_increment, |
| full_name varchar(255), |
| age integer, |
| PRIMARY KEY (id) |
| ) TYPE=InnoDB; |
| |
| The fields map one to one to the members of the Person class. Using the |
| InnoDB type is not required. We strongly recommend it however, since it |
| supports transactions. The id column is of the type auto_increment. This is |
| required for the id generator that we will use. Other id generators may have |
| other requirements to work as expected. |
| |
| In order for PersistentObject to be able to store objects of the Person class |
| into the persons table, we need to tell it how the columns are mapped to class |
| members. We will use ezcPersistentCodeManager to fetch the definitions when |
| required. |
| |
| ezcPersistentCodeManager requires us to define the mapping using the |
| ezcPersistentObjectDefinition, ezcPersistentObjectIdProperty and |
| ezcPersistentObjectProperty classes:: |
| |
| <?php |
| $def = new ezcPersistentObjectDefinition(); |
| $def->table = "persons"; |
| $def->class = "Person"; |
| |
| $def->idProperty = new ezcPersistentObjectIdProperty; |
| $def->idProperty->columnName = 'id'; |
| $def->idProperty->propertyName = 'id'; |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( 'ezcPersistentNativeGenerator' ); |
| |
| $def->properties['name'] = new ezcPersistentObjectProperty; |
| $def->properties['name']->columnName = 'full_name'; |
| $def->properties['name']->propertyName = 'name'; |
| $def->properties['name']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_STRING; |
| |
| $def->properties['age'] = new ezcPersistentObjectProperty; |
| $def->properties['age']->columnName = 'age'; |
| $def->properties['age']->propertyName = 'age'; |
| $def->properties['age']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_INT; |
| |
| return $def; |
| ?> |
| |
| The first block of code creates the definition object and sets the database |
| table and the name of the class to map. The second block defines the mapping of |
| the identifier member and the algorithm that should be used to create |
| identifiers for new objects. We will use ezcPersistentNativeGenerator, which |
| simply retrieves the new id generated by auto_increment. If you rely on a |
| database backend that does not support auto_increment (e.g. Oracle), |
| ezcPersistentSequenceGenerator is the class to choose here. |
| |
| The next two code blocks define the mapping between the database columns and |
| the class members. It is possible to use the same name in the class and the |
| database for a field. |
| |
| The members must be inserted into the properties member, which is an |
| associative array, using the name of the member as the key name. Note that this |
| must not necessarily be the same name as the database column the property |
| corresponds to. It is required that you use the property name here! |
| |
| If you look at the API for ezcPersistentObjectDefinition, it also has a |
| property named "columns" that is the same array as the "properties", except |
| that it is mapped to the column names instead of the property names. This |
| reverse mapping is set up by ezcPersistentCodeManager automatically. |
| |
| Finally, we return the complete definition. Your definition will not work unless |
| you return it to the manager. |
| |
| To make the definition work with the ezcPersistentCodeManager, it must be put in |
| a separate PHP file and given the name of the class in lowercase letters. In |
| our example, the filename should be person.php. |
| |
| For classes in namespaces (PHP 5.3 and newer), sub-directories are used for the |
| namespaces. That means, the persistence definition for a class |
| ``\My\Namespace\Person`` must reside in the file ``my/namespace/person.php``. |
| |
| The session object |
| ------------------ |
| |
| The session object is in charge of the actual loading and saving of persistent |
| objects. A session can be created by simply instantiating it:: |
| |
| <?php |
| $db = ezcDbFactory::create( 'mysql://user:password@host/database' ); |
| $session = new ezcPersistentSession( |
| $db, |
| new ezcPersistentCacheManager( new ezcPersistentCodeManager( "path/to/definitions" ) ) |
| ); |
| ?> |
| |
| The session takes two arguments: a pointer to the database instance to use and |
| the manager from which to retrieve persistent object definitions. You can also |
| use a Database instance by using the ezcDbInstance class. You can then simply |
| use ezcDbInstance::get(); instead of passing $db as argument to the |
| ezcPersistentSession's constructor. |
| |
| We are using ezcPersistentCodeManager to load the definitions directly from |
| file. You should point it to the location where you saved the person.php file. |
| If you have several directories containing definitions, you can use the |
| ezcPersistentMultiManager class to add as many as you like. In addition to the |
| code manager we use a cache manager. The cache manager makes sure the |
| definition is loaded from disk only once. |
| |
| While it is possible to create a new session each time you want to manipulate a |
| persistent object, you will probably want to use the same session each |
| time. This functionality can be achieved by using the |
| ezcPersistentSessionInstance class:: |
| |
| <?php |
| $session = new ezcPersistentSession( ezcDbInstance::get(), |
| new ezcPersistentCacheManager( new ezcPersistentCodeManager( ... ) ) ); |
| ezcPersistentSessionInstance::set( $session ); // set default session |
| |
| // retrieve the session |
| $session = ezcPersistentSessionInstance::get(); |
| ?> |
| |
| Lazy initialization |
| ------------------- |
| |
| Lazy initialization is a mechanism to load and configure a component, only |
| when it is really used in your application. This mechanism saves time for |
| parsing the classes and configuration, when the component is not used at all |
| during one request. You can find a description how you can use it for your |
| own components and how it works in the `ezcBase tutorial`__. The keyword for |
| the database component is ezcInitPersistentSessionInstance. |
| |
| __ ../Base/tutorial.html#lazy-initialization |
| |
| .. include:: tutorial_lazy_initialization.php |
| :literal: |
| |
| ezcBaseInit::setCallback accepts as a first parameter a component specific key, |
| which lets the component later request the right configuration callback. The |
| second parameter is the name of the class to perform the static callback on. |
| This class must implement the ezcBaseConfigurationInitializer class. |
| Each component's lazy initialization calls the static method configureObject() |
| on the referenced class. |
| |
| This example shows a way to configure multiple database handlers, only when |
| they are really requested in your application. The example does basically the |
| same like the first example in this tutorial, but creates the connection not |
| before it is really required. |
| |
| In line 32 the default persistent session is first requested in this example, |
| which does not exist yet, so that the configuration class earlier referenced |
| through the setCallback() call will be asked for a new instance for the |
| current instance name, which is 'null' for the default instance. |
| |
| In the configureObject() method in line 8 we switch on the instance name and |
| create and return the right newly created database handler. Line 35 shows, |
| that this will also work with multiple database instances, creating an |
| additional persistent session that reads the definition files from another |
| directory (additionalPersistent). |
| |
| Creating and updating an object |
| ------------------------------- |
| |
| Creating a new Person object and making it persistent is straightforward:: |
| |
| <?php |
| $object = new Person(); |
| $object->name = "Guybrush Threepwood"; |
| $object->age = 31; |
| |
| $session->save( $object ); |
| ?> |
| |
| This code saves our newly created object to the database and generates an id |
| for it. The id is set to the id property of the object. Since Guybrush is our |
| first person, he is assigned the id of 1. |
| |
| Of course, the age of Guybrush Threepwood is the source of much debate, and he |
| is probably younger than 31. To change his age, simply edit the object and |
| tell the session to update it. :: |
| |
| <?php |
| $object->age = 25; |
| $session->update( $object ); |
| ?> |
| |
| Note that we used update() to store the object this time. This is because we |
| want to trigger an UPDATE query instead of an INSERT query. |
| |
| Finding objects |
| ---------------- |
| |
| There are several ways to retrieve persistent objects from the database. The |
| simplest is to fetch one object by its identifier. :: |
| |
| <?php |
| $object = $session->load( 'Person', 1 ); |
| ?> |
| |
| This code retrieves the Guybrush object created above. |
| |
| If you have stored a lot of persistent objects to the database and want to retrieve a |
| list, you can use the find() method. The find() method requires a query parameter |
| that can first be retrieved from the session. :: |
| |
| <?php |
| $q = $session->createFindQuery( 'Person' ); |
| $q->where( $q->expr->gt( 'age', $q->bindValue( 15 ) ) ) |
| ->orderBy( 'full_name' ) |
| ->limit( 10 ); |
| $objects = $session->find( $q, 'Person' ); |
| ?> |
| |
| This code will fetch a maximum of 10 Person objects where the age is |
| higher than 15, sorted by name. |
| |
| This is achieved by manipulating the query object returned by |
| ezcPersistentSession->createFindQuery(). To learn more about query abstraction |
| and how to use it, please refer to the `specific subsection of the Database |
| components tutorial`_. |
| |
| .. _`specific subsection of the Database components tutorial`: introduction_Database.html#query-abstraction-usage |
| |
| The find() method will fetch the complete result set and instantiate it for |
| you. This is not desirable if you are fetching large numbers of objects and you |
| want it to be fast and efficient. For this you can use the findIterator() |
| method:: |
| |
| <?php |
| $q = $session->createFindQuery( 'Person' ); |
| $q->where( $q->expr->gt( 'age', $q->bindValue( 15 ) ) ) |
| ->orderBy( 'name' ) |
| ->limit( 10 ); |
| $objects = $session->findIterator( $q, 'Person' ); |
| |
| foreach( $objects as $object ) |
| { |
| // ... |
| } |
| ?> |
| |
| This code will produce the same result as the first find() example. However, |
| only one object will be instantiated and the data will be transferred from the |
| database only when it is needed. |
| |
| The final example uses a find query with a logical and to find objects:: |
| |
| <?php |
| $q = $session->createFindQuery( 'Person' ); |
| $q->where( |
| $q->expr->lAnd( |
| $q->expr->eq( 'name', $q->bindValue( 'Guybrush Threepwood' ) ), |
| $q->expr->eq( 'age', $q->bindValue( 25 ) ) |
| ) |
| ); |
| $objects = $session->findIterator( $q, 'Person' ); |
| foreach( $objects as $object ) |
| { |
| // ... |
| } |
| ?> |
| |
| Deleting objects |
| ----------------- |
| |
| The easiest way to delete persistent objects is to use the delete() method on |
| the session:: |
| |
| <?php |
| $object = $session->load( 'Person', 1 ); |
| $session->delete( $object ); |
| ?> |
| |
| Of course, you can only delete instantiated objects this way. If you want to |
| delete an object or a whole series of objects that are not instantiated, you can |
| use the deleteFromQuery() method:: |
| |
| <?php |
| $q = $session->createDeleteQuery( 'Person' ); |
| $q->where( $q->expr->gt( 'age', $q->bindValue( 15 ) ) ); |
| $session->deleteFromQuery( $q ); |
| ?> |
| |
| The above code will remove all persons from the database who are more than 15 |
| years old. |
| |
| |
| Identifier generation |
| ===================== |
| |
| All persistent objects must have an identifier field. The identifier |
| generation algorithm defines how the system will generate ids for new |
| objects. This chapter describes the available generators. |
| |
| ezcPersistentSequenceGenerator |
| ------------------------------ |
| |
| The sequence generator relies on the PDO::lastInsertId() method to retrieve the |
| ids for newly created persistent objects. |
| |
| For databases supporting auto_increment (like MySQL and SQLite), use |
| ezcPersistentNativeGenerator. Other databases must use a sequence. For example, |
| the PostgreSQL person table definition should be as follows:: |
| |
| CREATE TABLE persons |
| ( |
| id integer unsigned not null, |
| full_name varchar(255), |
| age integer, |
| PRIMARY KEY (id) |
| ); |
| CREATE SEQUENCE person_seq START 1; |
| |
| If your database requires you to use a sequence, this parameter should be |
| provided to ezcPersistentSequenceGenerator in the mapping definition. :: |
| |
| <?php |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( |
| 'ezcPersistentSequenceGenerator', |
| array( 'sequence' => 'person_sequence' ) |
| ); |
| ?> |
| |
| ezcPersistentNativeGenerator |
| ---------------------------- |
| |
| The native generator relies on auto_increment, which is supported by, among |
| others, MySQL and SQLite. An example table definition looks like this:: |
| |
| CREATE TABLE persons |
| ( |
| id integer unsigned not null auto_increment, |
| full_name varchar(255), |
| age integer, |
| PRIMARY KEY (id) |
| ); |
| |
| The corresponding generator definition is below:: |
| |
| <?php |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( |
| 'ezcPersistentNativeGenerator' |
| ); |
| ?> |
| |
| ezcPersistentManualGenerator |
| ---------------------------- |
| |
| If you do not rely on a database mechanism to generate values for a primary |
| key column, you have to use the ezcPersistentManualGenerator class. You can |
| then set the value of the id property by hand and save the object afterwards. |
| |
| For example:: |
| |
| CREATE TABLE persons |
| ( |
| login varchar(25), |
| full_name varchar(255), |
| age integer, |
| PRIMARY KEY (login) |
| ); |
| |
| In this table, the string value is used as the primary key. Therefore, we have |
| to generate id values manually. Use the following definition:: |
| |
| <?php |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( |
| 'ezcPersistentManualGenerator' |
| ); |
| ?> |
| |
| For saving a new instance, use the following code:: |
| |
| <?php |
| $object = new Person(); |
| |
| // Manually set the id |
| $object->login = "guybrush"; |
| |
| $object->name = "Guybrush Threepwood"; |
| $object->age = 31; |
| |
| $session->save( $object ); |
| ?> |
| |
| Definition loaders |
| =================== |
| |
| The session object needs to be able to fetch the persistent object |
| definitions in order to function properly. The task of fetching the definitions |
| is performed by a definition loader, which is provided to the session when it is |
| instantiated. |
| |
| ezcPersistentCodeManager |
| ------------------------- |
| |
| This is currently the only manager available. It simply reads the definition |
| from a file with the same name as the class from the specified directory. It does |
| not perform any error checking on the definition and simply assumes that it is |
| correct. |
| |
| |
| Extending the definition loader |
| ------------------------------- |
| |
| It is very easy to create your own definition loader. Simply extend the |
| ezcPersistentDefinitionManager abstract class and implement the |
| fetchDefinition() method:: |
| |
| <?php |
| class ezcPersistentCodeManager extends ezcPersistentDefinitionManager |
| { |
| public function fetchDefinition( $class ) |
| { |
| } |
| } |
| ?> |
| |
| The fetchDefinition() method should create the definition structure for the |
| requested class or throw an exception. |
| |
| Relations |
| ========= |
| |
| Relations are defined within the persistence mapping. |
| |
| Relation class overview |
| ----------------------- |
| |
| The following definition classes are available to realize object relations: |
| |
| ezcPersistentOneToManyRelation |
| This class is used to define 1:n relations. For example, one person might be |
| related to multiple addresses, but one address might only be related to |
| one person. |
| |
| ezcPersistentManyToManyRelation |
| Using this class, you can define n:m relations. For example, a person can be |
| related to multiple addresses, while an address can be related to multiple |
| persons. |
| |
| ezcPersistentOneToOneRelation |
| With this class you can define 1:1 relations, which might be useful for |
| slight de-normalization. For example, you can split your user data |
| from the user credentials. |
| |
| ezcPersistentManyToOneRelation |
| This relation (n:1) does not make sense on its own, but as the reverse |
| connection for a 1:n relation. |
| |
| ezcPersistentRelationCollection |
| This class allows you to define multiple relations to the same PHP class. |
| |
| All of these classes extend the abstract class ezcPersistentRelation. |
| |
| Relations basics |
| ---------------- |
| |
| For the examples in this section, we will reuse the Person class, defined in |
| `The persistent class`_. In addition, we will use an Address class, which looks |
| as follows:: |
| |
| <?php |
| class Address |
| { |
| public $id = null; |
| public $street = null; |
| public $zip = null; |
| public $city = null; |
| |
| public function setState( array $state ) |
| { |
| foreach ( $state as $key => $value ) |
| { |
| $this->$key = $value; |
| } |
| } |
| |
| public function getState() |
| { |
| return array( |
| "id" => $this->id, |
| "street" => $this->street, |
| "zip" => $this->zip, |
| "city" => $this->city, |
| ); |
| } |
| } |
| ?> |
| |
| The Address class will be extended later on to include relations. The |
| following basic persistence mapping is used and extended for each example:: |
| |
| <?php |
| $def = new ezcPersistentObjectDefinition(); |
| $def->table = "addresses"; |
| $def->class = "Address"; |
| |
| $def->idProperty = new ezcPersistentObjectIdProperty; |
| $def->idProperty->columnName = 'id'; |
| $def->idProperty->propertyName = 'id'; |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( 'ezcPersistentSequenceGenerator' ); |
| |
| $def->properties['street'] = new ezcPersistentObjectProperty; |
| $def->properties['street']->columnName = 'street'; |
| $def->properties['street']->propertyName = 'street'; |
| $def->properties['street']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_STRING; |
| |
| $def->properties['zip'] = new ezcPersistentObjectProperty; |
| $def->properties['zip']->columnName = 'zip'; |
| $def->properties['zip']->propertyName = 'zip'; |
| $def->properties['zip']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_STRING; |
| |
| $def->properties['city'] = new ezcPersistentObjectProperty; |
| $def->properties['city']->columnName = 'city'; |
| $def->properties['city']->propertyName = 'city'; |
| $def->properties['city']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_STRING; |
| |
| return $def; |
| ?> |
| |
| Defining a simple relation |
| -------------------------- |
| |
| The following extensions are necessary for the given class and persistence |
| mapping, to realize a simple 1:n relation. Each person will be able to have |
| multiple addresses, but one address may only refer to one person. |
| |
| The Address class needs to be enhanced as follows, to store the id of the |
| Person it is related to. :: |
| |
| <?php |
| class Address |
| { |
| // ... |
| |
| private $person; |
| |
| // ... |
| |
| public function getState() |
| { |
| return array( |
| // ... |
| "person" => $this->person, |
| ); |
| } |
| } |
| ?> |
| |
| Additionally, we need to define the new property $person in the persistence |
| mapping of the Address class:: |
| |
| <?php |
| // ... |
| |
| $def->properties['person'] = new ezcPersistentObjectProperty; |
| $def->properties['person']->columnName = 'person_id'; |
| $def->properties['person']->propertyName = 'person'; |
| $def->properties['person']->propertyType = ezcPersistentObjectProperty::PHP_TYPE_INT; |
| |
| ?> |
| |
| The relation definition takes place in the persistence mapping of the Person |
| class in `The persistent class`_. It needs to be extended as follows:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentOneToManyRelation( |
| "persons", |
| "addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentSingleTableMap( |
| "id", |
| "person_id" |
| ) |
| ); |
| |
| // .. |
| ?> |
| |
| A relation to another persistent object is defined in the property |
| ezcPersistentObjectDefinition $relations, which is an array. Each relation must |
| have the name of the persistent object class it refers to as the key in this |
| array. An instance of one of the classes shown in `Class overview`_ must be the |
| value. In this case, it is ezcPersistentOneToManyRelation. The parameter to its |
| constructor are the names of the tables that the relation refers to. The first |
| table is the table of the current object, and the second one refers to the related |
| object. |
| |
| To define which properties are used to realize the relation mapping, the |
| property ezcPersistentOneToManyRelation->columnMap is used. It contains an |
| array of (in this case) ezcPersistentSingleTableMap, which maps one column of |
| each of the tables to one column of another. In the above case, the |
| database column "id" from the table "persons" would be mapped to the column |
| "person_id" in the table "addresses". In general, this means, that "id" is the |
| primary key from the "persons" table and "person_id" is the foreign key in the |
| "addresses" table, that refers to the "persons" table. Please note that the |
| relation mappings are done on the table name/column name and not on the |
| class name/property name. |
| |
| If you want to map using several columns, you can add more |
| ezcPersistentSingleTableMap instances to the columnMap array. For example, if |
| you are using a person's first and last name as the primary key for the |
| "persons" table, you could define the relation like this:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentOneToManyRelation( |
| "persons", |
| "addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentSingleTableMap( |
| "firstname", |
| "person_firstname" |
| ), |
| new ezcPersistentSingleTableMap( |
| "lastname", |
| "person_lastname" |
| ) |
| ); |
| |
| return $def; |
| ?> |
| |
| Using a relation |
| ---------------- |
| |
| To use the previously defined 1:n relation, ezcPersistentSession offers several |
| new methods: |
| |
| ezcPersistentSession->getRelatedObject() |
| This method can be used to retrieve a single related object to a given source |
| object. If no related object can be found, it will throw an exception. |
| |
| ezcPersistentSession->getRelatedObjects() |
| In contrast to ezcPersistentSession->getRelatedObject(), this method always |
| returns an array of all related objects. It will not throw an exception if no |
| related object can be found, but will instead return an empty array. |
| |
| ezcPersistentSession->addRelatedObject() |
| Using this method, you can build a relation between two persistent objects. It |
| will set the defined properties on the objects, but does not store them to |
| the database automatically. (Exceptions are |
| ezcPersistentManyToManyRelation objects. Further details are below.) |
| |
| ezcPersistentSession->removeRelatedObject() |
| As the counterpart to ezcPersistentSession->addRelatedObject(), this method |
| is used to remove the relation between two objects. It will not store the given |
| objects for you, but only remove the necessary properties. (Again, exceptions |
| are ezcPersistentManyToManyRelation objects. Further details are |
| below.) |
| |
| Using these methods, we can now retrieve all addresses that are related to one |
| person:: |
| |
| <?php |
| $person = $session->load( "Person", 1 ); |
| $addresses = $session->getRelatedObjects( $person, "Address" ); |
| ?> |
| |
| The variable $addresses will then contain an array of all Address objects found |
| for the Person object with an id of 1. To relate these addresses to another |
| Person object, we can do the following:: |
| |
| |
| <?php |
| $personOld = $session->load( "Person", 1 ); |
| $personNew = $session->load( "Person", 23 ); |
| $addresses = $session->getRelatedObjects( $personOld, "Address" ); |
| |
| foreach ( $addresses as $address ) |
| { |
| $session->removeRelatedObject( $personOld, $address ); |
| $session->addRelatedObject( $personNew, $address ); |
| $session->update( $address ); |
| } |
| ?> |
| |
| Defining n:m relations |
| ---------------------- |
| |
| The ezcPersistentManyToManyRelation class works slightly different than |
| the other ezcPersistentRelation classes. For this kind of relation, you need an |
| extra table in your database to store the relation records. The next example |
| shows the definition of an ezcPersistentManyToManyRelation relation, based on |
| the Person and Address example classes. Each person can have several |
| addresses and each address can be used by several persons. You need to use the |
| original example classes for this, thus we do not need to extend them here. |
| The definition of the relational mapping for the Person class must be extended |
| as follows:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentManyToManyRelation( |
| "persons", |
| "addresses", |
| "persons_addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentDoubleTableMap( "id", "person_id", "address_id", "id" ) |
| ); |
| |
| return $def; |
| ?> |
| |
| In contrast to all other implementations of ezcPersistentRelation, the |
| ezcPersistentManyToManyRelation constructor expects three table names: |
| |
| - the name of the current objects table |
| - the name of the related objects table |
| - the table name to store the relation records |
| |
| A similar exception applies to columnMap of this relation definition. It |
| consists of ezcPersistentDoubleTableMap instances, which carry four column names |
| each. The first column is the column to choose from the source table (usually |
| its primary key). The second column defines the column in your relation table |
| that maps to the first column. In our example, the column "id" from the |
| "persons" table maps to the column "person_id" from the relation table |
| "persons_addresses". The same applies to the third and fourth columns. The third |
| column defines the column of the relation table that maps to the fourth column |
| given. The fourth column specifies the column of your destination table to use for |
| mapping. In our example, the relation table "persons_addresses" has a column |
| "address_id", which is a foreign key referring to the column "id" in the table |
| "addresses". |
| |
| As with ezcPersistentSingleTableMap instances, you can use multiple |
| mappings in one ezcPersistentManyToManyRelation->columnMap array. Here, we use |
| a person's first and last name for the mapping:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentManyToManyRelation( |
| "persons", |
| "addresses", |
| "persons_addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentDoubleTableMap( "firstname", "person_firstname", "address_id", "id" ), |
| new ezcPersistentDoubleTableMap( "lastname", "person_lastname", "address_id", "id" ) |
| ); |
| |
| return $def; |
| ?> |
| |
| Using n:m relations |
| ------------------- |
| |
| As stated earlier, the usage methods behave slightly differently when dealing |
| with n:m relations. If you use ezcPersistentSession->addRelatedObject(), the |
| desired relation record is inserted into the relation table. The same applies to the |
| removeRelatedObject() method of ezcPersistentSession, which deletes the specific |
| record. This also means that you do not need to store the affected objects |
| explicitly after altering the relations between them. If you have made other |
| changes to the objects, they must be stored to save the changes. |
| |
| Aside from that, the ezcPersistentSession->delete() method keeps track of the |
| relation records. If you delete a record, all of its relation records are |
| automatically deleted. |
| |
| Special 1:1 relations |
| --------------------- |
| |
| If you want to use 1:1 relations, where two tables share a common primary key, |
| you need to define a table to generate the key and the other table to use |
| ezcPersistentManualGenerator. For our example, if one person may only have one |
| address, the definition would be as follows:: |
| |
| <?php |
| $def = new ezcPersistentObjectDefinition(); |
| $def->table = "addresses"; |
| $def->class = "Address"; |
| |
| $def->idProperty = new ezcPersistentObjectIdProperty; |
| $def->idProperty->columnName = 'id'; |
| $def->idProperty->propertyName = 'id'; |
| $def->idProperty->generator = new ezcPersistentGeneratorDefinition( 'ezcPersistentManualGenerator' ); |
| |
| return $def; |
| ?> |
| |
| This is the relation (defined in the definition file of the Person):: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentOneToOneRelation( |
| "persons", |
| "addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentSingleTableMap( |
| "id", |
| "person_id" |
| ) |
| ); |
| |
| return $def; |
| ?> |
| |
| If you let both tables use ezcPersistentSequenceGenerator for the same key, |
| ezcPersistentSession will fail to save a related object, since the id will |
| already be set by the ezcPersistentSession::addRelatedObject() method. |
| |
| Another way to make this work is to not use the same primary key for both |
| tables, but to make the Address object have its own id and only use the Person |
| id as a foreign key. |
| |
| Reverse relations |
| ----------------- |
| |
| Since you can always look at a relation from two sides, ezcPersistentRelation |
| implementations can be configured to be "reverse". A reverse relation |
| indicates that the relation is already defined in the opposite direction and |
| that the original direction is the main used one. The one |
| marked as "reverse" is a secondary one, for consistency reasons. For a relation |
| that is marked as reverse, it is not possible to use |
| ezcPersistentSession->addRelatedObject() and |
| ezcPersistentSession->removeRelatedObject(). You can still use |
| ezcPersistentSession->getRelatedObjects() for relations that are flagged |
| "reverse". |
| |
| For most relation types, the reverse attribute of the relation definition object |
| is set to false by default. You can manually set it. Exceptions are |
| ezcPersistentManyToOneRelation relations. This relation type only makes sense |
| as a reverse relation for ezcPersistentOneToManyRelation. Therefore, the |
| reverse attribute is set to true for ezcPersistentManyToOneRelation and is not |
| publicly accessible for writing. |
| |
| The following example shows the reverse relation definition for the n:m relations |
| example:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Person"] = new ezcPersistentManyToManyRelation( |
| "addresses", |
| "persons", |
| "persons_addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentDoubleTableMap( "id", "address_id", "person_id", "id" ), |
| ); |
| $def->relations["Address"]->reverse = true; |
| |
| return $def; |
| ?> |
| |
| With the relation definition shown above, you would still be able to relate the |
| Persons object to an Address object, but not to add or remove related |
| Person objects to/from an Address. In other words, the following code still |
| works:: |
| |
| <?php |
| $address = $session->load( "Address", 23 ); |
| $persons = $session->getRelatedObjects( $address, "Person" ); |
| // ... |
| ?> |
| |
| While the following would not work:: |
| |
| <?php |
| // ... |
| foreach ( $persons as $person ) |
| { |
| $session->removeRelatedObject( $address, $person ); |
| } |
| ?> |
| |
| Instead, only the other direction works, because this one is the main |
| direction. :: |
| |
| <?php |
| // ... |
| foreach ( $persons as $person ) |
| { |
| $session->removeRelatedObject( $person, $address ); |
| } |
| ?> |
| |
| Cascading deletes |
| ----------------- |
| |
| Cascading relations are done through the flag "cascade" of a |
| ezcPersistentRelation implementation. All implementations except the |
| ezcPersistentManyToManyRelation class support this flag. It allows you to |
| automatically delete all related objects for a source object when the source |
| object is deleted. |
| |
| The following example shows how to add a cascading relation to a relation |
| definition, based on the example from `Defining a simple relation`_:: |
| |
| <?php |
| // ... |
| |
| $def->relations["Address"] = new ezcPersistentOneToManyRelation( |
| "persons", |
| "addresses" |
| ); |
| $def->relations["Address"]->columnMap = array( |
| new ezcPersistentSingleTableMap( |
| "id", |
| "person_id" |
| ), |
| ); |
| $def->relations["Address"]->cascade = true; |
| |
| return $def; |
| ?> |
| |
| If you now use the following, the Person object and all related Address objects |
| are deleted:: |
| |
| <?php |
| $person = $session->load( "Person", 1 ); |
| $session->delete( $person ); |
| ?> |
| |
| Beware that this does not work with ezcPersistentManyToManyRelation instances, |
| because it could cause serious inconsistencies in your data. |
| |
| Defining multiple relations to the same PHP class |
| ------------------------------------------------- |
| |
| In some cases it might be necessary to define multiple relations to the same |
| PHP class. An example for this can be seen when enhancing the Person example |
| from `The persistence mapping`_ as follows:: |
| |
| CREATE TABLE persons |
| ( |
| id integer unsigned not null auto_increment, |
| full_name varchar(255), |
| age integer, |
| mother integer, |
| father integer, |
| PRIMARY KEY (id) |
| ) |
| |
| Here each person is connected to 2 objects of the same table: The mother and |
| the father. Since only 1 PHP class per table is desired, the class needs to be |
| referenced by the Person class twice. |
| |
| Assuming that the 2 new properties of the Person class have been defined |
| correctly in the persistence mapping, the following code can be used to achieve |
| the desired relations:: |
| |
| <?php |
| // ... $def is the persistence defintion |
| |
| $relations = new ezcPersistentRelationCollection(); |
| |
| // Mother relation |
| |
| $relations['mothers_children'] = new ezcPersistentOneToManyRelation( |
| 'PO_person', |
| 'PO_person' |
| ); |
| $relations['mothers_children']->columnMap = array( |
| new ezcPersistentSingleTableMap( 'id', 'mother' ) |
| ); |
| $relations['mothers_children']->cascade = true; |
| |
| $relations['mother'] = new ezcPersistentManyToOneRelation( |
| 'PO_person', |
| 'PO_person' |
| ); |
| $relations['mother']->columnMap = array( |
| new ezcPersistentSingleTableMap( 'mother', 'id' ) |
| ); |
| |
| // .. |
| |
| $def->relations['Person'] = $relations; |
| ?> |
| |
| 2 relations need to be defined to reflect the relation between a mother an her |
| children. "mother" defines the relation from a child to its mother and |
| "mothers_children" defines the opposite direction, from a child to its mother. |
| Both relations operate on the Person class itself, therefore an |
| ezcPersistentRelationCollection is used to carry the relation definitions. |
| |
| Relations are defined as shown earlier in this section. The only difference |
| here is, that the relation definitions themselves are not added directly to the |
| $relations property of the ezcPersistentObjectDefinition instance. Instead they |
| are assigned to unique names on a relation collection, which is then added to |
| the $relations property of the object definition. |
| |
| The comment indicating further code (...) in the example above indicates that |
| further relations are missing in the collection: The relations "father" and |
| "fathers_children" are excluded here, since they work exactly like the |
| corresponding mother relations, above. A fifth relation is shown below:: |
| |
| <?php |
| $relations = new ezcPersistentRelationCollection(); |
| |
| // ... mother / father relations |
| |
| // Sibling relation |
| |
| $relations['siblings'] = new ezcPersistentManyToManyRelation( |
| "PO_person", |
| "PO_person", |
| "PO_sibling" |
| ); |
| $relations['siblings']->columnMap = array( |
| new ezcPersistentDoubleTableMap( "id", "person", "sibling", "id" ), |
| ); |
| |
| $def->relations['MultiRelationTestPerson'] = $relations; |
| |
| // assigning the relation collection |
| |
| $def->relations['Person'] = $relations; |
| ?> |
| |
| As can be seen above, a relation collection can carry an arbitrary number of |
| relations, which are of arbitrary type. |
| |
| Using multiple relations to the same PHP class |
| ---------------------------------------------- |
| |
| To make use of the relations to the Person class defined in the last section, |
| the name of the desired relation has to be submitted to all relation |
| operations:: |
| |
| <?php |
| $mother = $this->session->load( 'Person', 1 ); |
| $children = $this->session->getRelatedObjects( |
| $mother, |
| 'Person', |
| 'mothers_children' |
| ); |
| ?> |
| |
| The code above fetches all children of a mother, as defined by the relation |
| "mothers_children". The third parameter to |
| ezcPersistentSession->getRelatedObject() is mandatory in this case. If you |
| leave it out, a ezcPersistentUndeterministicRelationException will be thrown. |
| The parameter is ignored if you submit it when not working with a relation |
| collection. |
| |
| In the same manor a new related object can only be added if the affected |
| relation is submitted to ezcPersistentSession->addRelatedObject() as shown |
| below:: |
| |
| <?php |
| $newChild = new Person(); |
| $newChild->name = "New child"; |
| |
| $this->session->save( $newChild ); |
| |
| $this->session->addRelatedObject( |
| $mother, |
| $newChild, |
| 'mothers_children' |
| ); |
| |
| // Make relation changes take effect |
| $this->session->save( $newChild ); |
| ?> |
| |
| Identity mapping |
| ================ |
| |
| The `identity map pattern`__, as described by Martin Fowler, ensures that only |
| 1 PHP object with the same identity exists. That means, if you load an object |
| from the database a second time, the originally created instance is re-used |
| instead of creating a new instance. In addition to that, identity mapping can |
| potentially save SQL queries and therefore reduce database load. |
| |
| Note that with identity mapping, the methods *updateFromQuery()* and |
| *deleteFromQuery()* will result in a complete reset of the identity map. |
| Further details on that can be found under `Effects of identity mapping`_. |
| |
| .. __: http://martinfowler.com/eaaCatalog/identityMap.html |
| |
| Activate identity mapping |
| ------------------------- |
| |
| Identity mapping is activated by wrapping an instance of |
| ezcPersistentSessionIdentityDecorator around your existing |
| ezcPersistentSession:: |
| |
| <?php |
| // $originalSession contains a valid ezcPersistentSession |
| |
| $identityMap = new ezcPersistentBasicIdentityMap( |
| $originalSession->definitionManager |
| ); |
| $session = new ezcPersistentSessionIdentityDecorator( |
| $originalSession, |
| $identityMap |
| ); |
| ?> |
| |
| You can transparently exchange ezcPersistentSession and |
| ezcPersistentSessionIdentityDecorator inside your application, since their APIs |
| do not differ. |
| |
| One point where you might experience problems are *instanceof* checks for |
| ezcPersistentSession. Just replace these with checks for |
| ezcPersistentSessionFoundation. |
| |
| Effects of identity mapping |
| --------------------------- |
| |
| Identity mapping avoids that you have 2 different PHP objects with the same |
| database identity in your application. For example:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $person = $identitySession->load( 'Person', 23 ); |
| |
| // ... somewhere else in your app ... |
| |
| $samePerson = $identitySession->load( 'Person', 23 ); |
| ?> |
| |
| The variables *$person* and *$samePerson* are both references to the very same |
| object. In fact, the second call to *$identitySession->load()* does not issue a |
| database query at all, but just returns the existing instance of the desired |
| *Person* object, since this has already been loaded before. |
| |
| Identity mapping also affects finding of persistent objects:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $person = $identitySession->load( 'Person', 23 ); |
| |
| $person->name = 'New Name'; |
| |
| // ... somewhere else in your app ... |
| |
| $query = $identitySession->createFindQuery( 'Person' ); |
| $persons = $identitySession->find( $query ); |
| ?> |
| |
| The call to *$identitySession->find()* fetches all *Person* objects from the |
| database. This includes the *Person* with ID 23, which has already been loaded |
| before. Due to that, there is not a second instance of this object in |
| *$persons*, but the existing instance is re-used. This does not save you an SQL |
| query, but avoids inconsistencies. The change of the *$name* property of the |
| *Person* object with ID 23 is reflected in the found objects, although the |
| object has not been saved, yet. |
| |
| **Note:** You should not use the methods *updateFromQuery()* and |
| *deleteFromQuery()* with ezcPersistentSessionIdentityDecorator. If you use any |
| of these methods, all cached objects will automatically be removed from the |
| identity map, because the effects of these methods can not be traced by the |
| mechanism. The reset of the identity map might result in unexpected |
| inconsistencies, which should originally be avoided by the mechanism. |
| |
| Related objects |
| --------------- |
| |
| Identity mapping also affects the loading of related objects:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $person = $identitySession->load( 'Person', 23 ); |
| $addresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| // ... somewhere else in your app ... |
| |
| $sameAddresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| ?> |
| |
| The variable *$sameAddresses* contains the very same array of *Address* objects |
| as *$addresses*. In fact, no second database call is issued, to fetch the |
| related objects, since they have already been loaded before. |
| |
| This also works, if you add or delete related objects:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $person = $identitySession->load( 'Person', 23 ); |
| $addresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| // Add a new address |
| $newAddress = new Address(); |
| $identitySession->addRelatedObject( |
| $person, |
| $newAddress |
| ); |
| |
| // Remove an address |
| $identitySession->removeRelatedObject( |
| $person, |
| $addresses[42] |
| ); |
| |
| // ... somewhere else in your app ... |
| |
| $sameAddresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| ?> |
| |
| There is no additional database query performed on the second call to |
| *$identitySession->getRelatedObjects()* here, too. Still, the *$sameAddresses* |
| reflects the operations of adding a new *Address* to and removing the *Address* |
| with ID *42* from the list of related objects. Even if these operations have |
| not been reflected in the database, yet. |
| |
| Pre-fetching of related objects |
| ------------------------------- |
| |
| Using the identity map decorator allows you to pre-fetch related objects, using |
| a JOIN. There are 2 different methods that |
| ezcPersistentSessionIdentityDecorator has in addition to ezcPersistentSession. |
| The first one allows you to load a certain object together with related |
| objects:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $prefetchRelations = array( |
| 'addresses' => new ezcPersistentRelationFindDefinition( |
| 'Address', |
| null, |
| array( |
| 'cities' => new ezcPersistentRelationFindDefinition( |
| 'City' |
| ) |
| ) |
| ), |
| 'purchases' => new ezcPersistentRelationFindDefinition( |
| 'Purchase' |
| ) |
| ); |
| |
| $person = $identitySession->loadWithRelatedObjects( |
| 'Person', |
| 23, |
| $prefetchRelations |
| ); |
| |
| // ... somewhere else in your app ... |
| |
| $addresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| foreach ( $addresses as $address ) |
| { |
| $city = $identitySession->getRelatedObjects( |
| $address, |
| 'City' |
| ); |
| } |
| |
| // ... somewhere else in your app ... |
| |
| $purchases = $identitySession->getRelatedObjects( |
| $person, |
| 'Purchase' |
| ); |
| |
| ?> |
| |
| The method *$idSession->loadWithRelatedObjects()* allows you to load a certain |
| object and its related objects (included nesting). |
| |
| The array *$prefetchRelations* defines, which related objects are fetched. The |
| first ezcPersistentRelationFindDefinition in the array instructs to load |
| related objects of type *Address*. The second parameter to the constructor of |
| ezcPersistentRelationFindDefinition can be used to specify the relation name, |
| if you use multiple relations to the same PHP class. The third parameter is |
| used to define deeper relations. In the example above, it defines that for each |
| loaded *Address* object, the related object of class *City* is loaded. |
| |
| The second element of the *$prefetchRelations* array defines that for the |
| object to load, also all related objects of the *Purchase* class are loaded. |
| This loading happens in a single SQL statement, using multiple JOIN statements. |
| |
| After defining the relations to fetch, the *Person* with ID *23* is loaded, |
| including the defined related objects. Therefore, none of the latter calls to |
| *$idSession->getRelatedObjects()* results in a database query, since all of |
| these related object sets have already been loaded in a single SQL query. |
| |
| In a similar way, you can find a set of objects including their related |
| objects. Assume that *$prefetchRelations* is defined in the same way as in the |
| last example:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| // $prefetchRelations = array( ... ); |
| |
| $query = $identitySession->createFindQueryWithRelations( |
| 'Person', |
| $prefetchRelations |
| ); |
| $persons = $identitySession->find( $query ); |
| |
| // ... somewhere else in your app ... |
| |
| foreach ( $persons as $person ) |
| { |
| $addresses = $identitySession->getRelatedObjects( |
| $person, |
| 'Address' |
| ); |
| |
| foreach ( $addresses as $address ) |
| { |
| $city = $identitySession->getRelatedObjects( |
| $address, |
| 'City' |
| ); |
| } |
| |
| $purchases = $identitySession->getRelatedObjects( |
| $person, |
| 'Purchase' |
| ); |
| } |
| |
| ?> |
| |
| In this case, all *Person* objects are loaded from the database, including |
| their related objects as defined. Therefore, none of the calls to |
| *$identitySession->getRelatedObjects()* leads to a database query, but just |
| returns the pre-fetched set of related objects. |
| |
| Related object sub-sets |
| ----------------------- |
| |
| In many cases it is not desirable to load all objects of a certain class and |
| their related objects. Therefore it is possible, to restrict the loaded |
| objects, using the typical query manipulations. However, this might often mean, |
| that the set of related objects fetched for an object does not contain all |
| related objects of a type, but only a sub-set. |
| |
| To work around this potential inconsistencies, the concept of *named related |
| object sub-sets* was introduced:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $prefetchRelations = array( |
| 'custom_addresses' => new ezcPersistentRelationFindDefinition( |
| 'Address' |
| ), |
| ); |
| |
| $query = $identitySession->createFindQueryWithRelations( |
| 'Person', |
| $prefetchRelations |
| ); |
| $query->where( |
| $query->expr->gte( |
| 'custom_addresses_zip', |
| 40000 |
| ), |
| $query->expr->lte( |
| 'custom_addresses_zip', |
| 50000 |
| ), |
| ); |
| |
| $persons = $identitySession->find( $query ); |
| |
| // ... somewhere else in your app ... |
| |
| foreach ( $persons as $person ) |
| { |
| $addresses = $identitySession->getRelatedObjectSubset( |
| $person, |
| 'custom_addresses' |
| ); |
| } |
| |
| ?> |
| |
| In this example the query to fetch *Person* objects and their related *Address* |
| objects has been restricted by a WHERE clause: Only the persons of which the |
| *Address* lies between ZIP code *40000* and *50000* are fetched and only these |
| addresses are loaded as related objects. A person that has two addresses, one |
| with ZIP code *42235* and the other with *12345*, will be loaded with this |
| query, but only one of its addresses is loaded. This is obviously not the full |
| set of related objects of type *Address* for this person. Storing this set of |
| *Address* objects in the identity map as usual would lead to inconsistencies. |
| |
| Therefore, whenever you use a WHERE condition to restrict the set of loaded |
| objects, a *named related object sub-set* is created. As can be seen in the |
| example above, to fetch the pre-loaded *Address* objects for a *Person*, the |
| method *$identity->getRelatedObjectSubset()* is used instead of |
| *getRelatedObjects()*. This method receives the alias you have chosen before |
| for a certain sub-set in the *$prefetchRelations* array. *'custom_addresses'* |
| is the array key used to define the pre-fetching of *Address* objects. |
| |
| For this reasons, the array keys used in the *$prefetchRelations* array must be |
| unique on all levels, if you want to restrict the pre-fetching query using a |
| WHERE condition. As shown in the example above, the keys used to define related |
| objects to be loaded, are also used, to access these specific relations in the |
| query. *'custom_addresses_zip'* is used to restrict on the property *$zip* of |
| the *Address* objects to be fetched in the set *custom_addresses*. |
| |
| **Note:** Normal sets of related objects and named sub-sets can exist beside |
| each other in the identity map. Whenever you use *addRelatedObject()*, all |
| named related objects sub-sets will be removed from the identity map, since the |
| mechanism can not detect to which of these the object must be added and to |
| which not. However, using *removeRelatedObject()* and *delete()* do not have |
| this side-effect, and will simply be reflected in the named sets, too. |
| |
| Creating named sub-sets of related objects also works with the |
| *createRelationFindQuery()* method, which already exists in |
| ezcPersistentSession. This method optionally receives a 4th parameter |
| *$setName* on ezcPersistentSessionIdentityDecorator, to define the set to store |
| related objects found:: |
| |
| <?php |
| // $identitySession is an ezcPersistentSessionIdentityDecorator |
| |
| $person = $identitySession->load( 'Person', 23 ); |
| |
| $query = $identitySession->createRelationFindQuery( |
| $person, |
| 'Address', |
| null, |
| 'custom_addresses' |
| ); |
| |
| $query->where( |
| $query->expr->gte( |
| 'zip', |
| 40000 |
| ), |
| $query->expr->lte( |
| 'zip', |
| 50000 |
| ), |
| ); |
| |
| $customAddresses = $identitySession->find( $query ); |
| |
| // ... some where else in your app ... |
| |
| $sameCustomAddresses = $identitySession->getRelatedObjectSubset( |
| $person, |
| 'custom_addresses' |
| ); |
| |
| ?> |
| |
| Retrieving the named sub-set of related objects works the same way before. |
| |
| Note that there is no need to prefix the property names in the WHERE condition |
| in this case. Since only one level of related objects can be loaded here, it is |
| not necessary. Note also, that in this case, *Address* objects are only loaded |
| for one specific *Person* and are not pre-fetched for any other *Person* |
| object. Trying to access the created named related object sub-set for another |
| *Person* will return *null* since these to do not exist. |
| |
| You can also access the named related object sub-set just created, by using an |
| equaling relation find query (which uses the same set name!). The identity |
| mapping mechanism detects that you want to load this specific named set, |
| determines that this is already loaded and simply returns it, instead of |
| issuing another database query. |
| |
| |
| .. |
| Local Variables: |
| mode: rst |
| fill-column: 79 |
| End: |
| vim: et syn=rst tw=79 |