eZ components - PersistentObject ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. contents:: Table of Contents Introduction ============ Persistent Object provides object persistence using a database for PHP 5. Persistent Object relies uses the Database components to provide database abstraction. It does not rely on code generation and does not force a specific inheritance structure to work. Persistent Object is built to be fast and flexible allowing you to build your persistent classes in the same way as any other class in your application. Class overview ============== This section gives you an overview of the main classes of Persistent Object. ezcPersistentSession ezcPersistentSession is the main API for interaction with the object persistence. Loading, saving and deleting persistent objects is done through this class. Basic Usage =========== This chapter describes typical usage of the Persistent Object package 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 persistent object. It is a simple class with only a few members: :: id; $result['name'] = $this->name; $result['age'] = $this->age; return $result; } public function setState( array $properties ) { foreach( $state 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 e.g access methods or properties or even having 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 be prepared to only set one member at the time. 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 Persistent Object 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 the ezcPersistentCodeManager to fetch the definitions when required. ezcPersistentCodeManager requires us to define the mapping using the ezcPersistentObjectDefinition, ezcPersistentObjectIdProperty and ezcPersistentObjectProperty classes: :: table = "persons"; $def->class = "Person"; $def->idProperty = new ezcPersistentObjectIdProperty; $def->idProperty->columnName = 'id'; $def->idProperty->propertyName = 'id'; $def->idProperty->generator = new ezcPersistentGeneratorDefinition( 'ezcPersistentSequenceGenerator' ); $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 the ezcPersistentSequenceGenerator which simply retrieves the new identifier generated by auto_increment. 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. If you look at the API of ezcPersistentObjectDefinition it also has a property named 'columns' which is the same array as the 'properties' except it is mapped on the column names instead of the property names. This reverse mapping is set up by the ezcPersistentCodeManager. 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 it should be put in a file named 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 simply by instantiating it: :: The session takes two arguments: a pointer to the database instance to use and the manager it should use to retrieve persistent object definitions. We are using the ezcPersistentCodeManager which loads the definitions directly from file. You should point it to the location where you saved the person.php file. 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 wrapping the session in a singleton class. Creating and updating an object ------------------------------- Creating a new Person object and making it persistent is straight forward: :: 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 will get the identifier 1. Of course, the age of Guybrush Threepwood is the source of much debate and most probably he is younger than 31. To make the change simply edit the object and tell the session to update it. :: 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. :: load( 'Person', 1 ); ?> This code retrieves the Guybrush object created above. If you have stored a lot of persistent objects to the database and you want to retrieve a list you can use the find method. The find method requires a query parameter which you can retrieve from the session first. :: createFindQuery( 'Person' ); $q->where( $q->expr->gt( 'age', 15 ) ) ->orderBy( 'full_name' ) ->limit( 10 ); $objects = $session->find( $q, 'Person' ); ?> This code will fill fetch a maximum of 10 Person objects older than 15 years sorted by their names. 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 fetchIterator() method: :: createFindQuery( 'Person' ); $q->where( $q->expr->gt( 'age', 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 transfered from the database only when it is needed. Deleting objects ----------------- The easiest way to delete persistent objects is to use the delete method() on the 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: :: createDeleteQuery( 'Person' ); $q->where( $q->expr->gt( 'age', 15 ) ); $session->deleteFromQuery( $q ); ?> When executed the above code will remove all persons older than 15 years old from the database. Identifier generation ===================== All persistent objects are required to 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. It is recommended to use auto_increment id columns for databases supporting it. This includes MySQL and SQLite. Other databases need to use a sequence. E.g for PostgreSQL the person table definition should look like: :: 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 the ezcPersistentSequenceGenerator in the mapping definition. :: idProperty->generator = new ezcPersistentGeneratorDefinition( 'ezcPersistentSequenceGenerator', array( 'sequence' => 'person_sequence' ) ); ?> 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 named the same 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: :: The fetchDefinition() method should create the definition structure for the requested class or throw an exception .. Local Variables: mode: rst fill-column: 79 End: vim: et syn=rst tw=79