Modified: trunk/PersistentObject/src/persistent_session.php ============================================================================== --- trunk/PersistentObject/src/persistent_session.php [iso-8859-1] (original) +++ trunk/PersistentObject/src/persistent_session.php [iso-8859-1] Fri Jan 11 13:07:22 2008 @@ -44,300 +44,95 @@ */ public function __construct( ezcDbHandler $db, ezcPersistentDefinitionManager $manager ) { - $this->properties['database'] = $db; + $this->properties['database'] = $db; $this->properties['definitionManager'] = $manager; - } - - /** - * Sets the property $name to $value. - * - * @throws ezcBasePropertyNotFoundException - * if the property does not exist. - * - * @param string $name - * @param mixed $value - * - * @ignore - */ - public function __set( $name, $value ) - { - switch ( $name ) - { - case 'database': - case 'definitionManager': - throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ ); - break; - default: - throw new ezcBasePropertyNotFoundException( $name ); - break; - } - - } - - /** - * Returns the property $name. - * - * @throws ezcBasePropertyNotFoundException - * if the property does not exist. - * - * @param string $name - * @return mixed - * - * @ignore - */ - public function __get( $name ) - { - switch ( $name ) - { - case 'database': - case 'definitionManager': - return isset( $this->properties[$name] ) ? $this->properties[$name] : null; - break; - - default: - throw new ezcBasePropertyNotFoundException( $name ); - break; - } - } - - /** - * Deletes the persistent object $object. - * - * This method will perform a DELETE query based on the identifier of the - * persistent object $object. After delete() the ID property of $object - * will be reset to null. It is possible to [EMAIL PROTECTED] save()} $object - * afterwards. $object will then be stored with a new ID. - * - * If you defined relations for the given object, these will be checked to - * be defined as cascading. If cascading is configured, the related objects - * with this relation will be deleted, too. - * - * Relations that support cascading are: - * <ul> - * <li>[EMAIL PROTECTED] ezcPersistenOneToManyRelation}</li> - * <li>[EMAIL PROTECTED] ezcPersistenOneToOne}</li> - * </ul> - * - * @throws ezcPersistentDefinitionNotFoundxception - * if $the object is not recognized as a persistent object. - * @throws ezcPersistentObjectNotPersistentException - * if the object is not persistent already. + $this->properties['loadHandler'] = new ezcPersistentLoadHandler( $this ); + $this->properties['saveHandler'] = new ezcPersistentSaveHandler( $this ); + $this->properties['deleteHandler'] = new ezcPersistentDeleteHandler( $this ); + } + + /** + * Returns the persistent object of class $class with id $id. + * + * @throws ezcPersistentObjectException + * if the object is not available. + * @throws ezcPersistentObjectException + * if there is no such persistent class. + * + * @param string $class + * @param int $id + * + * @return object + */ + public function load( $class, $id ) + { + return $this->loadHandler->load( $class, $id ); + } + + /** + * Returns the persistent object of class $class with id $id. + * + * This method is equivalent to [EMAIL PROTECTED] load()} except that it returns null + * instead of throwing an exception if the object does not exist. + * + * @param string $class + * @param int $id + * + * @return object|null + */ + public function loadIfExists( $class, $id ) + { + return $this->loadHandler->loadIfExists( $class, $id ); + } + + /** + * Loads the persistent object with the id $id into the object $object. + * + * The class of the persistent object to load is determined by the class + * of $object. + * + * @throws ezcPersistentObjectException + * if the object is not available. + * @throws ezcPersistentDefinitionNotFoundException + * if $object is not of a valid persistent object type. * @throws ezcPersistentQueryException - * if the object could not be deleted. - * - * @param object $object The persistent object to delete. - */ - public function delete( $object ) - { - $class = get_class( $object ); - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - $state = $this->getObjectState( $object ); - $idValue = $state[$def->idProperty->propertyName]; - - // check that the object is persistent already - if ( $idValue == null || $idValue < 0 ) - { - $class = get_class( $object ); - throw new ezcPersistentObjectNotPersistentException( $class ); - } - - // Transaction savety for exceptions thrown while cascading - $this->database->beginTransaction(); - - try - { - // check for cascading relations to follow - foreach ( $def->relations as $relatedClass => $relation ) - { - $this->cascadeDelete( $object, $relatedClass, $relation ); - } - } - catch ( Exception $e ) - { - // Roll back the current transaction on any exception - $this->database->rollback(); - throw $e; - } - - // create and execute query - $q = $this->database->createDeleteQuery(); - $q->deleteFrom( $this->database->quoteIdentifier( $def->table ) ) - ->where( $q->expr->eq( $this->database->quoteIdentifier( $def->idProperty->columnName ), - $q->bindValue( $idValue ) ) ); - - try - { - $this->performQuery( $q, true ); - } - catch ( Exception $e ) - { - $this->database->rollback(); - throw $e; - } - - // After recursion of cascades everything should be fine here, or this - // final commit call should perform the rollback ordered by a deeper level - $this->database->commit(); - } - - /** - * Perform the cascading of deletes on a specific relation. - * - * This method checks a given $relation of a given $object for necessary - * actions on a cascaded delete and performs them. - * - * @param object $object The persistent object. - * @param string $relatedClass The class of the related persistent - * object. - * @param ezcPersistentRelation $relation The relation to check. - * - * @todo Revise cascading code. So far it sends 1 delete statement per - * object but we can also collect them table wise and send just 1 - * for each table. - */ - private function cascadeDelete( $object, $relatedClass, ezcPersistentRelation $relation ) - { - // Remove relation records for ManyToMany relations - if ( $relation instanceof ezcPersistentManyToManyRelation ) - { - foreach ( $this->getRelatedObjects( $object, $relatedClass ) as $relatedObject ) - { - // Need to determine the correct direction for removal - if ( $relation->reverse === true ) - { - $this->removeRelatedObject( $relatedObject, $object ); - } - else - { - $this->removeRelatedObject( $object, $relatedObject ); - } - } - } - if ( isset( $relation->cascade ) && $relation->cascade === true ) - { - if ( isset( $relation->reverse ) && $relation->reverse === true ) - { - throw new ezcPersistentRelationOperationNotSupported( - $class, - $relatedClass, - "cascade on delete", - "Reverse relations do not support cascading." - ); - } - foreach ( $this->getRelatedObjects( $object, $relatedClass ) as $relatedObject ) - { - $this->delete( $relatedObject ); - } - } - } - - /** - * Returns a delete query for the given persistent object $class. - * - * The query is initialized to delete from the correct table and - * it is only neccessary to set the where clause. - * - * Example: - * <code> - * $q = $session->createDeleteQuery( 'Person' ); - * $q->where( $q->expr->gt( 'age', $q->bindValue( 15 ) ) ); - * $session->deleteFromQuery( $q ); - * </code> - * - * @throws ezcPersistentObjectException - * if there is no such persistent class. - * - * @param string $class - * - * @return ezcQueryDelete - */ - public function createDeleteQuery( $class ) - { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - - // init query - $q = $this->database->createDeleteQuery(); - $q->setAliases( $this->generateAliasMap( $def, false ) ); - $q->deleteFrom( $this->database->quoteIdentifier( $def->table ) ); - - return $q; - } - - /** - * Deletes persistent objects using the query $query. - * - * The $query should be created using [EMAIL PROTECTED] createDeleteQuery()}. - * - * Currently this method only executes the provided query. Future - * releases PersistentSession may introduce caching of persistent objects. - * When caching is introduced it will be required to use this method to run - * cusom delete queries. To avoid being incompatible with future releases it is - * advisable to always use this method when running custom delete queries on - * persistent objects. - * - * @throws ezcPersistentQueryException - * if the delete query failed. - * - * @param ezcQueryDelete $query - */ - public function deleteFromQuery( ezcQueryDelete $query ) - { - $this->performQuery( $query ); - } - - /** - * Returns an update query for the given persistent object $class. - * - * The query is initialized to update the correct table and - * it is only neccessary to set the correct values. - * - * @throws ezcPersistentDefinitionNotFoundException - * if there is no such persistent class. - * - * @param string $class - * - * @return ezcQueryUpdate - */ - public function createUpdateQuery( $class ) - { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - - // init query - $q = $this->database->createUpdateQuery(); - $q->setAliases( $this->generateAliasMap( $def, false ) ); - $q->update( $this->database->quoteIdentifier( $def->table ) ); - - return $q; - } - - /** - * Updates persistent objects using the query $query. - * - * The $query should be created using createUpdateQuery(). - * - * Currently this method only executes the provided query. Future - * releases PersistentSession may introduce caching of persistent objects. - * When caching is introduced it will be required to use this method to run - * cusom delete queries. To avoid being incompatible with future releases it is - * advisable to always use this method when running custom delete queries on - * persistent objects. - * - * @throws ezcPersistentQueryException - * if the update query failed. - * - * @param ezcQueryUpdate $query - */ - public function updateFromQuery( ezcQueryUpdate $query ) - { - $this->performQuery( $query ); - } - - /** - * Returns a select query for the given persistent object $class. - * - * The query is initialized to fetch all columns from the correct table and - * has correct alias mappings between columns and property names of the - * persistent $class. + * if the find query failed. + * + * @param object $object + * @param int $id + */ + public function loadIntoObject( $object, $id ) + { + return $this->loadHandler->loadIntoObject( $object, $id ); + } + + /** + * Syncronizes the contents of $object with those in the database. + * + * Note that calling this method is equavalent with calling [EMAIL PROTECTED] + * loadIntoObject()} on $object with the id of $object. Any changes made + * to $object prior to calling refresh() will be discarded. + * + * @throws ezcPersistentObjectException + * if $object is not of a valid persistent object type. + * @throws ezcPersistentObjectException + * if $object is not persistent already. + * @throws ezcPersistentObjectException + * if the select query failed. + * + * @param object $object + */ + public function refresh( $object ) + { + return $this->loadHandler->refresh( $object ); + } + + /** + * Returns the result of the query $query as a list of objects. + * + * Returns the persistent objects found for $class using the submitted + * $query. $query should be created using [EMAIL PROTECTED] createFindQuery()} to + * ensure correct alias mappings and can be manipulated as needed. * * Example: * <code> @@ -345,39 +140,6 @@ * $allPersons = $session->find( $q, 'Person' ); * </code> * - * @throws ezcPersistentObjectException - * if there is no such persistent class. - * - * @param string $class - * - * @return ezcQuerySelect - */ - public function createFindQuery( $class ) - { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - - // init query - $q = $this->database->createSelectQuery(); - $q->setAliases( $this->generateAliasMap( $def ) ); - $q->select( $this->getColumnsFromDefinition( $def ) ) - ->from( $this->database->quoteIdentifier( $def->table ) ); - - return $q; - } - - /** - * Returns the result of the query $query as a list of objects. - * - * Returns the persistent objects found for $class using the submitted - * $query. $query should be created using [EMAIL PROTECTED] createFindQuery()} to - * ensure correct alias mappings and can be manipulated as needed. - * - * Example: - * <code> - * $q = $session->createFindQuery( 'Person' ); - * $allPersons = $session->find( $q, 'Person' ); - * </code> - * * If you are retrieving large result set, consider using [EMAIL PROTECTED] * findIterator()} instead. * @@ -404,328 +166,7 @@ */ public function find( ezcQuerySelect $query, $class ) { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - - $rows = $this->performQuery( $query )->fetchAll( PDO::FETCH_ASSOC ); - - // convert all the rows states and then objects - $result = array(); - foreach ( $rows as $row ) - { - $object = new $def->class; - $object->setState( - ezcPersistentStateTransformer::rowToStateArray( $row, $def ) - ); - $result[] = $object; - } - return $result; - } - - /** - * Returns the related objects of a given $relatedClass for an $object. - * - * This method returns the related objects of type $relatedClass for the - * given $object. This method (in contrast to [EMAIL PROTECTED] getRelatedObject()}) - * always returns an array of found objects, no matter if only 1 object - * was found (e.g. [EMAIL PROTECTED] ezcPersistentManyToOneRelation}), none or several - * ([EMAIL PROTECTED] ezcPersistentManyToManyRelation}). - * - * Example: - * <code> - * $person = $session->load( "Person", 1 ); - * $relatedAddresses = $session->getRelatedObjects( $person, "Address" ); - * echo "Number of addresses found: " . count( $relatedAddresses ); - * </code> - * - * Relations that should preferably be used with this method are: - * <ul> - * <li>[EMAIL PROTECTED] ezcPersistentOneToManyRelation}</li> - * <li>[EMAIL PROTECTED] ezcPersistentManyToManyRelation}</li> - * </ul> - * For other relation types [EMAIL PROTECTED] getRelatedObject()} is recommended. - * - * @param object $object - * @param string $relatedClass - * - * @return array(int=>object($relatedClass)) - * - * @throws ezcPersistentRelationNotFoundException - * if the given $object does not have a relation to $relatedClass. - */ - public function getRelatedObjects( $object, $relatedClass ) - { - $query = $this->createRelationFindQuery( $object, $relatedClass ); - return $this->find( $query, $relatedClass ); - } - - /** - * Returns the related object of a given $relatedClass for an $object. - * - * This method returns the related object of type $relatedClass for the - * object $object. This method (in contrast to [EMAIL PROTECTED] getRelatedObjects()}) - * always returns a single result object, no matter if more related objects - * could be found (e.g. [EMAIL PROTECTED] ezcPersistentOneToManyRelation}). If no - * related object is found, an exception is thrown, while [EMAIL PROTECTED] - * getRelatedObjects()} just returns an empty array in this case. - * - * Example: - * <code> - * $person = $session->load( "Person", 1 ); - * $relatedAddress = $session->getRelatedObject( $person, "Address" ); - * echo "Address of this person: " . $relatedAddress->__toString(); - * </code> - * - * Relations that should preferably be used with this method are: - * <ul> - * <li>[EMAIL PROTECTED] ezcPersistentManyToOneRelation}</ li> - * <li>[EMAIL PROTECTED] ezcPersistentOneToOneRelation}</li> - * </ul> - * For other relation types [EMAIL PROTECTED] getRelatedObjects()} is recommended. - * - * @param object $object - * @param string $relatedClass - * - * @return object($relatedClass) - * - * @throws ezcPersistentRelationNotFoundException - * if the given $object does not have a relation to $relatedClass. - */ - public function getRelatedObject( $object, $relatedClass ) - { - $query = $this->createRelationFindQuery( $object, $relatedClass ); - // This method only needs to return 1 object - $query->limit( 1 ); - - $resArr = $this->find( $query, $relatedClass ); - if ( sizeof( $resArr ) < 1 ) - { - throw new ezcPersistentRelatedObjectNotFoundException( $object, $relatedClass ); - } - return $resArr[0]; - } - - /** - * Returns the base query for retrieving related objects. - * - * See [EMAIL PROTECTED] getRelatedObject()} and [EMAIL PROTECTED] getRelatedObjects()}. Can be - * modified by additional where conditions and simply be used with - * [EMAIL PROTECTED] find()} and the related class name, to retrieve a sub-set of - * related objects. - * - * @param object $object - * @param string $relatedClass - * - * @return ezcDbSelectQuery - * - * @throws ezcPersistentRelationNotFoundException - * if the given $object does not have a relation to $relatedClass. - */ - public function createRelationFindQuery( $object, $relatedClass ) - { - $def = $this->definitionManager->fetchDefinition( ( $class = get_class( $object ) ) ); - if ( !isset( $def->relations[$relatedClass] ) ) - { - throw new ezcPersistentRelationNotFoundException( $class, $relatedClass ); - } - $relation = $def->relations[$relatedClass]; - - $query = $this->createFindQuery( $relatedClass ); - - $objectState = $this->getObjectState( $object ); - - switch ( ( $relationClass = get_class( $relation ) ) ) - { - case "ezcPersistentOneToManyRelation": - case "ezcPersistentManyToOneRelation": - case "ezcPersistentOneToOneRelation": - foreach ( $relation->columnMap as $map ) - { - $query->where( - $query->expr->eq( - $this->database->quoteIdentifier( "{$map->destinationColumn}" ), - $query->bindValue( $objectState[$def->columns[$map->sourceColumn]->propertyName] ) - ) - ); - } - break; - case "ezcPersistentManyToManyRelation": - $query->from( $this->database->quoteIdentifier( $relation->relationTable ) ); - foreach ( $relation->columnMap as $map ) - { - $query->where( - $query->expr->eq( - $this->database->quoteIdentifier( $relation->relationTable ) . "." . $this->database->quoteIdentifier( $map->relationSourceColumn ), - $query->bindValue( $objectState[$def->columns[$map->sourceColumn]->propertyName] ) - ), - $query->expr->eq( - $this->database->quoteIdentifier( $relation->relationTable ) . "." . $this->database->quoteIdentifier( $map->relationDestinationColumn ), - $this->database->quoteIdentifier( $relation->destinationTable ) . "." . $this->database->quoteIdentifier( $map->destinationColumn ) - ) - ); - } - break; - default: - throw new ezcPersistentRelationInvalidException( $relationClass ); - } - return $query; - } - - /** - * Create a relation between $object and $relatedObject. - * - * This method is used to create a relation between the given source - * $object and the desired $relatedObject. The related object is not stored - * in the database automatically, only the desired properties are set. An - * exception is [EMAIL PROTECTED], where the relation - * record is stored automatically and there is no need to store - * $relatedObject explicitly after establishing the relation. - * - * @param object $object - * @param object $relatedObject - * - * @throws ezcPersistentRelationOperationNotSupportedException - * if a relation to create is marked as "reverse" [EMAIL PROTECTED] - * ezcPersistentRelation->reverse}. - * @throws ezcPersistentRelationNotFoundException - * if the deisred relation is not defined. - */ - public function addRelatedObject( $object, $relatedObject ) - { - $class = get_class( $object ); - $def = $this->definitionManager->fetchDefinition( ( $class = get_class( $object ) ) ); - - $relatedClass = get_class( $relatedObject ); - - $objectState = $this->getObjectState( $object ); - $relatedObjectState = $this->getObjectState( $relatedObject ); - - if ( !isset( $def->relations[$relatedClass] ) ) - { - throw new ezcPersistentRelationNotFoundException( $class, $relatedClass ); - } - if ( isset( $def->relations[$relatedClass]->reverse ) && $def->relations[$relatedClass]->reverse === true ) - { - throw new ezcPersistentRelationOperationNotSupportedException( - $class, - $relatedClass, - "addRelatedObject", - "Relation is a reverse relation." - ); - } - - $relatedDef = $this->definitionManager->fetchDefinition( get_class( $relatedObject ) ); - switch ( get_class( ( $relation = $def->relations[get_class( $relatedObject )] ) ) ) - { - case "ezcPersistentOneToManyRelation": - case "ezcPersistentOneToOneRelation": - foreach ( $relation->columnMap as $map ) - { - $relatedObjectState[$relatedDef->columns[$map->destinationColumn]->propertyName] = - $objectState[$def->columns[$map->sourceColumn]->propertyName]; - } - break; - case "ezcPersistentManyToManyRelation": - $q = $this->database->createInsertQuery(); - $q->insertInto( $this->database->quoteIdentifier( $relation->relationTable ) ); - $insertColumns = array(); - foreach ( $relation->columnMap as $map ) - { - if ( in_array( $map->relationSourceColumn, $insertColumns ) === false ) - { - $q->set( - $this->database->quoteIdentifier( $map->relationSourceColumn ), - $q->bindValue( $objectState[$def->columns[$map->sourceColumn]->propertyName] ) - ); - $insertColumns[] = $map->relationSourceColumn; - } - if ( in_array( $map->relationDestinationColumn, $insertColumns ) === false ) - { - $q->set( - $this->database->quoteIdentifier( $map->relationDestinationColumn ), - $q->bindValue( $relatedObjectState[$relatedDef->columns[$map->destinationColumn]->propertyName] ) - ); - $insertColumns[] = $map->relationDestinationColumn; - } - } - $this->performQuery( $q ); - break; - } - - $relatedObject->setState( $relatedObjectState ); - } - - /** - * Removes the relation between $object and $relatedObject. - * - * This method is used to delete an existing relation between 2 objects. - * Like [EMAIL PROTECTED] addRelatedObject()} this method does not store the related - * object after removing its relation properties (unset), except for [EMAIL PROTECTED] - * ezcPersistentManyToManyRelation()}s, for which the relation record is - * deleted from the database. - * - * @param object $object Source object of the relation. - * @param object $relatedObject Related object. - * - * @throws ezcPersistentRelationOperationNotSupportedException - * if a relation to create is marked as "reverse". - * @throws ezcPersistentRelationNotFoundException - * if the deisred relation is not defined. - */ - public function removeRelatedObject( $object, $relatedObject ) - { - $class = get_class( $object ); - $def = $this->definitionManager->fetchDefinition( ( $class = get_class( $object ) ) ); - - $relatedClass = get_class( $relatedObject ); - - if ( !isset( $def->relations[$relatedClass] ) ) - { - throw new ezcPersistentRelationNotFoundException( $class, $relatedClass ); - } - if ( isset( $def->relations[$relatedClass]->reverse ) && $def->relations[$relatedClass]->reverse === true ) - { - throw new ezcPersistentRelationOperationNotSupportedException( - $class, - $relatedClass, - "deleteRelation", - "Relation is a reverse relation." - ); - } - - $objectState = $this->getObjectState( $object ); - $relatedObjectState = $this->getObjectState( $relatedObject ); - - $relatedDef = $this->definitionManager->fetchDefinition( get_class( $relatedObject ) ); - switch ( get_class( ( $relation = $def->relations[get_class( $relatedObject )] ) ) ) - { - case "ezcPersistentOneToManyRelation": - case "ezcPersistentOneToOneRelation": - foreach ( $relation->columnMap as $map ) - { - $relatedObjectState[$relatedDef->columns[$map->destinationColumn]->propertyName] = null; - } - break; - case "ezcPersistentManyToManyRelation": - $q = $this->database->createDeleteQuery(); - $q->deleteFrom( $this->database->quoteIdentifier( $relation->relationTable ) ); - foreach ( $relation->columnMap as $map ) - { - $q->where( - $q->expr->eq( - $this->database->quoteIdentifier( $map->relationSourceColumn ), - $q->bindValue( $objectState[$def->columns[$map->sourceColumn]->propertyName] ) - ), - $q->expr->eq( - $this->database->quoteIdentifier( $map->relationDestinationColumn ), - $q->bindValue( $relatedObjectState[$relatedDef->columns[$map->destinationColumn]->propertyName] ) - ) - ); - } - $this->performQuery( $q ); - break; - } - - $relatedObject->setState( $relatedObjectState ); + return $this->loadHandler->find( $query, $class ); } /** @@ -755,144 +196,126 @@ */ public function findIterator( ezcQuerySelect $query, $class ) { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - $stmt = $this->performQuery( $query ); - return new ezcPersistentFindIterator( $stmt, $def ); - } - - /** - * Returns the persistent object of class $class with id $id. - * - * @throws ezcPersistentObjectException - * if the object is not available. + return $this->loadHandler->findIterator( $query, $class ); + } + + /** + * Returns the related objects of a given $relatedClass for an $object. + * + * This method returns the related objects of type $relatedClass for the + * given $object. This method (in contrast to [EMAIL PROTECTED] getRelatedObject()}) + * always returns an array of found objects, no matter if only 1 object + * was found (e.g. [EMAIL PROTECTED] ezcPersistentManyToOneRelation}), none or several + * ([EMAIL PROTECTED] ezcPersistentManyToManyRelation}). + * + * Example: + * <code> + * $person = $session->load( "Person", 1 ); + * $relatedAddresses = $session->getRelatedObjects( $person, "Address" ); + * echo "Number of addresses found: " . count( $relatedAddresses ); + * </code> + * + * Relations that should preferably be used with this method are: + * <ul> + * <li>[EMAIL PROTECTED] ezcPersistentOneToManyRelation}</li> + * <li>[EMAIL PROTECTED] ezcPersistentManyToManyRelation}</li> + * </ul> + * For other relation types [EMAIL PROTECTED] getRelatedObject()} is recommended. + * + * @param object $object + * @param string $relatedClass + * + * @return array(int=>object($relatedClass)) + * + * @throws ezcPersistentRelationNotFoundException + * if the given $object does not have a relation to $relatedClass. + */ + public function getRelatedObjects( $object, $relatedClass ) + { + return $this->loadHandler->getRelatedObjects( $object, $relatedClass ); + } + + /** + * Returns the related object of a given $relatedClass for an $object. + * + * This method returns the related object of type $relatedClass for the + * object $object. This method (in contrast to [EMAIL PROTECTED] getRelatedObjects()}) + * always returns a single result object, no matter if more related objects + * could be found (e.g. [EMAIL PROTECTED] ezcPersistentOneToManyRelation}). If no + * related object is found, an exception is thrown, while [EMAIL PROTECTED] + * getRelatedObjects()} just returns an empty array in this case. + * + * Example: + * <code> + * $person = $session->load( "Person", 1 ); + * $relatedAddress = $session->getRelatedObject( $person, "Address" ); + * echo "Address of this person: " . $relatedAddress->__toString(); + * </code> + * + * Relations that should preferably be used with this method are: + * <ul> + * <li>[EMAIL PROTECTED] ezcPersistentManyToOneRelation}</ li> + * <li>[EMAIL PROTECTED] ezcPersistentOneToOneRelation}</li> + * </ul> + * For other relation types [EMAIL PROTECTED] getRelatedObjects()} is recommended. + * + * @param object $object + * @param string $relatedClass + * + * @return object($relatedClass) + * + * @throws ezcPersistentRelationNotFoundException + * if the given $object does not have a relation to $relatedClass. + */ + public function getRelatedObject( $object, $relatedClass ) + { + return $this->loadHandler->getRelatedObject( $object, $relatedClass ); + } + + /** + * Returns a select query for the given persistent object $class. + * + * The query is initialized to fetch all columns from the correct table and + * has correct alias mappings between columns and property names of the + * persistent $class. + * + * Example: + * <code> + * $q = $session->createFindQuery( 'Person' ); + * $allPersons = $session->find( $q, 'Person' ); + * </code> + * * @throws ezcPersistentObjectException * if there is no such persistent class. * * @param string $class - * @param int $id - * - * @return object - */ - public function load( $class, $id ) - { - $def = $this->definitionManager->fetchDefinition( $class ); // propagate exception - $object = new $def->class; - $this->loadIntoObject( $object, $id ); - return $object; - } - - /** - * Returns the persistent object of class $class with id $id. - * - * This method is equivalent to [EMAIL PROTECTED] load()} except that it returns null - * instead of throwing an exception if the object does not exist. - * - * @param string $class - * @param int $id - * - * @return object|null - */ - public function loadIfExists( $class, $id ) - { - $result = null; - try - { - $result = $this->load( $class, $id ); - } - catch ( Exception $e ) - { - // eat, we return null on error - } - return $result; - } - - /** - * Loads the persistent object with the id $id into the object $object. - * - * The class of the persistent object to load is determined by the class - * of $object. - * - * @throws ezcPersistentObjectException - * if the object is not available. - * @throws ezcPersistentDefinitionNotFoundException - * if $object is not of a valid persistent object type. - * @throws ezcPersistentQueryException - * if the find query failed. - * - * @param object $object - * @param int $id - */ - public function loadIntoObject( $object, $id ) - { - $def = $this->definitionManager->fetchDefinition( get_class( $object ) ); // propagate exception - $q = $this->database->createSelectQuery(); - $q->select( $this->getColumnsFromDefinition( $def ) ) - ->from( $this->database->quoteIdentifier( $def->table ) ) - ->where( $q->expr->eq( $this->database->quoteIdentifier( $def->idProperty->columnName ), - $q->bindValue( $id ) ) ); - - $stmt = $this->performQuery( $q ); - $row = $stmt->fetch( PDO::FETCH_ASSOC ); - $stmt->closeCursor(); - if ( $row !== false ) // we got a result - { - // we could check if there was more than one result here - // but we don't because of the overhead and since the Persistent - // Object would be faulty by design in that case and the user would have - // to execute custom code to get into an invalid state. - try - { - $state = ezcPersistentStateTransformer::rowToStateArray( - $row, - $def - ); - } - catch ( Exception $e ) - { - throw new ezcPersistentObjectException( - "The row data could not be correctly converted to set data.", - "Most probably there is something wrong with a custom rowToStateArray implementation" - ); - } - $object->setState( $state ); - } - else - { - $class = get_class( $object ); - throw new ezcPersistentQueryException( "No object of class '$class' with id '$id'." ); - } - } - - /** - * Syncronizes the contents of $object with those in the database. - * - * Note that calling this method is equavalent with calling [EMAIL PROTECTED] - * loadIntoObject()} on $object with the id of $object. Any changes made - * to $object prior to calling refresh() will be discarded. - * - * @throws ezcPersistentObjectException - * if $object is not of a valid persistent object type. - * @throws ezcPersistentObjectException - * if $object is not persistent already. - * @throws ezcPersistentObjectException - * if the select query failed. - * - * @param object $object - */ - public function refresh( $object ) - { - $def = $this->definitionManager->fetchDefinition( get_class( $object ) ); // propagate exception - $state = $this->getObjectState( $object ); - $idValue = $state[$def->idProperty->propertyName]; - if ( $idValue !== null ) - { - $this->loadIntoObject( $object, $idValue ); - } - else - { - $class = get_class( $object ); - throw new ezcPersistentObjectNotPersistentException( $class ); - } + * + * @return ezcQuerySelect + */ + public function createFindQuery( $class ) + { + return $this->loadHandler->createFindQuery( $class ); + } + + /** + * Returns the base query for retrieving related objects. + * + * See [EMAIL PROTECTED] getRelatedObject()} and [EMAIL PROTECTED] getRelatedObjects()}. Can be + * modified by additional where conditions and simply be used with + * [EMAIL PROTECTED] find()} and the related class name, to retrieve a sub-set of + * related objects. + * + * @param object $object + * @param string $relatedClass + * + * @return ezcDbSelectQuery + * + * @throws ezcPersistentRelationNotFoundException + * if the given $object does not have a relation to $relatedClass. + */ + public function createRelationFindQuery( $object, $relatedClass ) + { + return $this->loadHandler->createRelationFindQuery( $object, $relatedClass ); } /** @@ -914,95 +337,21 @@ */ public function save( $object ) { - $this->saveInternal( $object ); - } - - /** - * Saves the new persistent object $object to the database using an INSERT INTO query. - * - * If $doPersistenceCheck is set this function will check if the object is persistent before - * saving. If not, the check is omitted. The correct ID is set to $object. - * - * @throws ezcPersistentObjectException - * if $object is not of a valid persistent object type. - * @throws ezcPersistentObjectException - * if $object is already stored to the database. - * @throws ezcPersistentObjectException - * if it was not possible to generate a unique identifier for the - * new object. - * @throws ezcPersistentObjectException - * if the insert query failed. - * - * @param object $object - * @param bool $doPersistenceCheck - * @param ezcPersistentIdentifierGenerator $idGenerator - */ - private function saveInternal( $object, $doPersistenceCheck = true, - ezcPersistentIdentifierGenerator $idGenerator = null ) - { - $def = $this->definitionManager->fetchDefinition( get_class( $object ) );// propagate exception - $state = $this->filterAndCastState( $this->getObjectState( $object ), $def ); - $idValue = $state[$def->idProperty->propertyName]; - - // fetch the id generator - if ( $idGenerator == null && ezcBaseFeatures::classExists( $def->idProperty->generator->class ) ) - { - $idGenerator = new $def->idProperty->generator->class; - if ( !( $idGenerator instanceof ezcPersistentIdentifierGenerator ) ) - { - throw new ezcPersistentIdentifierGenerationException( get_class( $object ), - "Could not initialize identifier generator: ". "{$def->idProperty->generator->class} ." ); - } - } - - if ( $doPersistenceCheck == true && $idGenerator->checkPersistence( $def, $this->database, $state ) ) - { - $class = get_class( $object ); - throw new ezcPersistentObjectAlreadyPersistentException( $class ); - } - - - // set up and execute the query - $q = $this->database->createInsertQuery(); - $q->insertInto( $this->database->quoteIdentifier( $def->table ) ); - foreach ( $state as $name => $value ) - { - if ( $name != $def->idProperty->propertyName ) // skip the id field - { - // set each of the properties - $q->set( $this->database->quoteIdentifier( $def->properties[$name]->columnName ), $q->bindValue( $value ) ); - } - } - - $this->database->beginTransaction(); - // let presave id generator do its work - $idGenerator->preSave( $def, $this->database, $q ); - - // execute the insert query - try - { - $this->performQuery( $q ); - } - catch ( Exception $e ) - { - $this->database->rollback(); - throw $e; - } - - // fetch the newly created id, and set it to the object - $id = $idGenerator->postSave( $def, $this->database ); - if ( $id === null ) - { - $this->database->rollback(); - throw new ezcPersistentIdentifierGenerationException( $def->class ); - } - - // everything seems to be fine, lets commit the queries to the database - // and update the object with its newly created id. - $this->database->commit(); - - $state[$def->idProperty->propertyName] = $id; - $object->setState( $state ); + return $this->saveHandler->save( $object ); + } + + /** + * Saves the new persistent object $object to the database using an UPDATE query. + * + * @throws ezcPersistentDefinitionNotFoundException if $object is not of a valid persistent object type. + * @throws ezcPersistentObjectNotPersistentException if $object is not stored in the database already. + * @throws ezcPersistentQueryException + * @param object $object + * @return void + */ + public function update( $object ) + { + return $this->saveHandler->update( $object ); } /** @@ -1025,98 +374,174 @@ */ public function saveOrUpdate( $object ) { - $def = $this->definitionManager->fetchDefinition( get_class( $object ) );// propagate exception - $state = $this->getObjectState( $object ); - - // fetch the id generator - $idGenerator = null; - if ( ezcBaseFeatures::classExists( $def->idProperty->generator->class ) ) - { - $idGenerator = new $def->idProperty->generator->class; - if ( !( $idGenerator instanceof ezcPersistentIdentifierGenerator ) ) - { - throw new ezcPersistentIdentifierGenerationException( get_class( $object ), - "Could not initialize identifier generator: ". "{$def->idProperty->generator->class} ." ); - } - } - - if ( !$idGenerator->checkPersistence( $def, $this->database, $state ) ) - { - $this->saveInternal( $object, false, $idGenerator ); - } - else - { - $this->updateInternal( $object, false ); - } - } - - /** - * Saves the new persistent object $object to the database using an UPDATE query. - * - * @throws ezcPersistentDefinitionNotFoundException if $object is not of a valid persistent object type. - * @throws ezcPersistentObjectNotPersistentException if $object is not stored in the database already. + return $this->saveHandler->saveOrUpdate( $object ); + } + + /** + * Create a relation between $object and $relatedObject. + * + * This method is used to create a relation between the given source + * $object and the desired $relatedObject. The related object is not stored + * in the database automatically, only the desired properties are set. An + * exception is [EMAIL PROTECTED], where the relation + * record is stored automatically and there is no need to store + * $relatedObject explicitly after establishing the relation. + * + * @param object $object + * @param object $relatedObject + * + * @throws ezcPersistentRelationOperationNotSupportedException + * if a relation to create is marked as "reverse" [EMAIL PROTECTED] + * ezcPersistentRelation->reverse}. + * @throws ezcPersistentRelationNotFoundException + * if the deisred relation is not defined. + */ + public function addRelatedObject( $object, $relatedObject ) + { + return $this->saveHandler->addRelatedObject( $object, $relatedObject ); + } + + /** + * Returns an update query for the given persistent object $class. + * + * The query is initialized to update the correct table and + * it is only neccessary to set the correct values. + * + * @throws ezcPersistentDefinitionNotFoundException + * if there is no such persistent class. + * + * @param string $class + * + * @return ezcQueryUpdate + */ + public function createUpdateQuery( $class ) + { + return $this->saveHandler->createUpdateQuery( $class ); + } + + /** + * Updates persistent objects using the query $query. + * + * The $query should be created using createUpdateQuery(). + * + * Currently this method only executes the provided query. Future + * releases PersistentSession may introduce caching of persistent objects. + * When caching is introduced it will be required to use this method to run + * cusom delete queries. To avoid being incompatible with future releases it is + * advisable to always use this method when running custom delete queries on + * persistent objects. + * * @throws ezcPersistentQueryException - * @param object $object - * @return void - */ - public function update( $object ) - { - $this->updateInternal( $object ); - } - - /** - * Saves the new persistent object $object to the database using an UPDATE query. - * - * If $doPersistenceCheck is set this function will check if the object is persistent before - * saving. If not, the check is omitted. - * - * @throws ezcPersistentDefinitionNotFoundException if $object is not of a valid persistent object type. - * @throws ezcPersistentObjectNotPersistentException if $object is not stored in the database already. + * if the update query failed. + * + * @param ezcQueryUpdate $query + */ + public function updateFromQuery( ezcQueryUpdate $query ) + { + return $this->saveHandler->updateFromQuery( $query ); + } + + /** + * Deletes the persistent object $object. + * + * This method will perform a DELETE query based on the identifier of the + * persistent object $object. After delete() the ID property of $object + * will be reset to null. It is possible to [EMAIL PROTECTED] save()} $object + * afterwards. $object will then be stored with a new ID. + * + * If you defined relations for the given object, these will be checked to + * be defined as cascading. If cascading is configured, the related objects + * with this relation will be deleted, too. + * + * Relations that support cascading are: + * <ul> + * <li>[EMAIL PROTECTED] ezcPersistenOneToManyRelation}</li> + * <li>[EMAIL PROTECTED] ezcPersistenOneToOne}</li> + * </ul> + * + * @throws ezcPersistentDefinitionNotFoundxception + * if $the object is not recognized as a persistent object. + * @throws ezcPersistentObjectNotPersistentException + * if the object is not persistent already. * @throws ezcPersistentQueryException - * @param object $object - * @param bool $doPersistenceCheck - * @return void - */ - private function updateInternal( $object, $doPersistenceCheck = true ) - { - $def = $this->definitionManager->fetchDefinition( get_class( $object ) ); // propagate exception - $state = $this->filterAndCastState( $this->getObjectState( $object ), $def ); - $idValue = $state[$def->idProperty->propertyName]; - - // fetch the id generator - $idGenerator = null; - if ( ezcBaseFeatures::classExists( $def->idProperty->generator->class ) ) - { - $idGenerator = new $def->idProperty->generator->class; - if ( !( $idGenerator instanceof ezcPersistentIdentifierGenerator ) ) - { - throw new ezcPersistentIdentifierGenerationException( get_class( $object ), - "Could not initialize identifier generator: ". "{$def->idProperty->generator->class} ." ); - } - } - - if ( $doPersistenceCheck == true && !$idGenerator->checkPersistence( $def, $this->database, $state ) ) - { - $class = get_class( $object ); - throw new ezcPersistentObjectNotPersistentException( get_class( $object ) ); - } - - // set up and execute the query - $q = $this->database->createUpdateQuery(); - $q->update( $this->database->quoteIdentifier( $def->table ) ); - foreach ( $state as $name => $value ) - { - if ( $name != $def->idProperty->propertyName ) // skip the id field - { - // set each of the properties - $q->set( $this->database->quoteIdentifier( $def->properties[$name]->columnName ), $q->bindValue( $value ) ); - } - } - $q->where( $q->expr->eq( $this->database->quoteIdentifier( $def->idProperty->columnName ), - $q->bindValue( $idValue ) ) ); - - $this->performQuery( $q ); - } + * if the object could not be deleted. + * + * @param object $object The persistent object to delete. + */ + public function delete( $object ) + { + return $this->deleteHandler->delete( $object ); + } + + /** + * Removes the relation between $object and $relatedObject. + * + * This method is used to delete an existing relation between 2 objects. + * Like [EMAIL PROTECTED] addRelatedObject()} this method does not store the related + * object after removing its relation properties (unset), except for [EMAIL PROTECTED] + * ezcPersistentManyToManyRelation()}s, for which the relation record is + * deleted from the database. + * + * @param object $object Source object of the relation. + * @param object $relatedObject Related object. + * + * @throws ezcPersistentRelationOperationNotSupportedException + * if a relation to create is marked as "reverse". + * @throws ezcPersistentRelationNotFoundException + * if the deisred relation is not defined. + */ + public function removeRelatedObject( $object, $relatedObject ) + { + return $this->deleteHandler->removeRelatedObject( $object, $relatedObject ); + } + + /** + * Deletes persistent objects using the query $query. + * + * The $query should be created using [EMAIL PROTECTED] createDeleteQuery()}. + * + * Currently this method only executes the provided query. Future + * releases PersistentSession may introduce caching of persistent objects. + * When caching is introduced it will be required to use this method to run + * cusom delete queries. To avoid being incompatible with future releases it is + * advisable to always use this method when running custom delete queries on + * persistent objects. + * + * @throws ezcPersistentQueryException + * if the delete query failed. + * + * @param ezcQueryDelete $query + */ + public function deleteFromQuery( ezcQueryDelete $query ) + { + return $this->deleteHandler->deleteFromQuery( $query ); + } + + /** + * Returns a delete query for the given persistent object $class. + * + * The query is initialized to delete from the correct table and + * it is only neccessary to set the where clause. + * + * Example: + * <code> + * $q = $session->createDeleteQuery( 'Person' ); + * $q->where( $q->expr->gt( 'age', $q->bindValue( 15 ) ) ); + * $session->deleteFromQuery( $q ); + * </code> + * + * @throws ezcPersistentObjectException + * if there is no such persistent class. + * + * @param string $class + * + * @return ezcQueryDelete + */ + public function createDeleteQuery( $class ) + { + return $this->deleteHandler->createDeleteQuery( $class ); + } + /** * Returns a hash map between property and column name for the given definition $def. @@ -1163,60 +588,6 @@ } /** - * Filters out all properties not in the definition and casts the - * values to native PHP types. - * - * @param array(string=>string) $state - * @param ezcPersistentObjectDefinition $def - * @return array(string=>mixed) - */ - private function filterAndCastState( array $state, ezcPersistentObjectDefinition $def ) - { - $typedState = array(); - foreach ( $state as $name => $value ) - { - $type = null; - if ( $name == $def->idProperty->propertyName ) - { - $type = $def->idProperty->propertyType; - $conv = null; - } - else - { - if ( !isset( $def->properties[$name] ) ) - { - continue; - } - $type = $def->properties[$name]->propertyType; - $conv = $def->properties[$name]->converter; - } - - if ( !is_null( $value ) ) - { - if ( !is_null( $conv ) ) - { - $value = $conv->toDatabase( $value ); - } - switch ( $type ) - { - case ezcPersistentObjectProperty::PHP_TYPE_INT: - $value = (int) $value; - break; - case ezcPersistentObjectProperty::PHP_TYPE_FLOAT: - $value = (float) $value; - break; - case ezcPersistentObjectProperty::PHP_TYPE_STRING: - $value = (string) $value; - break; - } - } - - $typedState[$name] = $value; - } - return $typedState; - } - - /** * Returns the object state. * * This method wraps around $object->getState() to add optional sanity @@ -1225,8 +596,10 @@ * * @param object $object * @return array - */ - private function getObjectState( $object ) + * + * @access private + */ + public function getObjectState( $object ) { // @todo Chekcs about object state should be added here, configurable. return $object->getState(); @@ -1240,8 +613,10 @@ * * @param ezcQuery $q * @return PDOStatement - */ - private function performQuery( ezcQuery $q ) + * + * @access private + */ + public function performQuery( ezcQuery $q ) { $this->database->beginTransaction(); try @@ -1262,5 +637,75 @@ throw new ezcPersistentQueryException( $e->getMessage(), $q ); } } + + /** + * Sets the property $name to $value. + * + * @throws ezcBasePropertyNotFoundException + * if the property does not exist. + * + * @param string $name + * @param mixed $value + * + * @ignore + */ + public function __set( $name, $value ) + { + switch ( $name ) + { + case 'database': + case 'definitionManager': + case 'loadHandler': + case 'saveHandler': + case 'deleteHandler': + throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ ); + break; + default: + throw new ezcBasePropertyNotFoundException( $name ); + break; + } + + } + + /** + * Property get access. + * + * Simply returns a given property. + * + * @throws ezcBasePropertyNotFoundException + * If a the value for the property propertys is not an instance of + * @param string $propertyName The name of the property to get. + * @return mixed The property value. + * + * @ignore + * + * @throws ezcBasePropertyNotFoundException + * if the given property does not exist. + * @throws ezcBasePropertyPermissionException + * if the property to be set is a write-only property. + */ + public function __get( $propertyName ) + { + if ( $this->__isset( $propertyName ) === true ) + { + return $this->properties[$propertyName]; + } + throw new ezcBasePropertyNotFoundException( $propertyName ); + } + + /** + * Returns if a property exists. + * + * Returns true if the property exists in the [EMAIL PROTECTED] $properties} array + * (even if it is null) and false otherwise. + * + * @param string $propertyName Option name to check for. + * @return void + * @ignore + */ + public function __isset( $propertyName ) + { + return array_key_exists( $propertyName, $this->properties ); + } } ?>
-- svn-components mailing list svn-components@lists.ez.no http://lists.ez.no/mailman/listinfo/svn-components