db = $db; $this->manager = $manager; } /** * Deletes the persistent object $pObject. * * This method will perform a DELETE query based on the identifier * of the persistent object. * After delete() the identifier in $pObject will be reset to null. * It is possible to save() $pObject afterwords. The object will then * be stored with a new id. * * @throws ezcPersistentDefinitionNotFoundxception if $the object is not recognized as a persistent object. * @throws ezcPersistentObjectNotPersistentException if the object is not persistent already. * @throws ezcPersistentQueryException if the object could not be deleted. * @param object $pObject * @return void */ public function delete( $pObject ) { $def = $this->manager->fetchDefinition( get_class( $pObject ) ); // propagate exception $state = $pObject->getState(); $idValue = $state[$def->idProperty->propertyName]; // check that the object is persistent already if ( $idValue == null || $idValue < 0 ) { $class = get_class( $pObject ); throw new ezcPersistentObjectNotPersistentException( $class ); } // create and execute query $q = $this->db->createDeleteQuery(); $q->deleteFrom( $def->table ) ->where( $q->expr->eq( $def->idProperty->columnName, $q->bindValue( $idValue ) ) ); try { $stmt = $q->prepare(); $stmt->execute(); if ( $stmt->errorCode() != 0 ) { throw new ezcPersistentQueryException( "The delete query failed." ); } } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } } /* * 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: * * $q = $session->createDeleteQuery(); * $q->where( $q->expr->gt( 'age', 15 ) ); * $session->deleteFromQuery( $q ); * * * @throws ezcPersistentObjectException if there is no such persistent class. * @param string $class * @return ezcQueryDelete */ public function createDeleteQuery( $class ) { $def = $this->manager->fetchDefinition( $class ); // propagate exception // init query $q = $this->db->createDeleteQuery(); $q->setAliases( $this->generateAliasMap( $def ) ); $q->deleteFrom( $def->table ); return $q; } /* * Deletes persistent objects using the query $query. * * The $query should be created using getDeleteQuery(). * * 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 * @return void */ public function deleteFromQuery( ezcQueryDelete $query ) { try { $stmt = $query->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } } /* * 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. * * Example: * * * * @throws ezcPersistentDefinitionNotFoundException if there is no such persistent class. * @param string $class * @return ezcQueryUpdate */ public function createUpdateQuery( $class ) { $def = $this->manager->fetchDefinition( $class ); // propagate exception // init query $q = $this->db->createUpdateQuery(); $q->setAliases( $this->generateAliasMap( $def ) ); $q->update( $def->table ); return $q; } /* * Updates persistent objects using the query $query. * * The $query should be created using getUpdateQuery(). * * 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 * @return void */ public function updateFromQuery( ezcQueryUpdate $query ) { try { $stmt = $query->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } } /** * Returns a select query for the given persistent object $class. * * The query is initialized to fetch all columns from the correct table. * * Example: * * $q = $session->createFindQuery( 'Person' ); * $allPersons = $session->find( $q, 'Person' ); * * * @throws ezcPersistentObjectException if there is no such persistent class. * @param string $class * @return ezcQuerySelect */ public function createFindQuery( $class ) { $def = $this->manager->fetchDefinition( $class ); // propagate exception // init query $q = $this->db->createSelectQuery(); $q->setAliases( $this->generateAliasMap( $def ) ); $q->select( '*' )->from( $def->table ); return $q; } /** * Returns the result of the query $query as a list of objects. * * Example: * * $q = $session->createFindQuery( 'Person' ); * $allPersons = $session->find( $q, 'Person' ); * * * If you are retrieving large result set, consider using findIterator() * instead. * * @throws ezcPersistentDefinitionNotFoundException if there is no such persistent class. * @throws ezcPersistentQueryException if the find query failed * @param ezcQuerySelect $query * @param string $class * @return array(object) */ public function find( ezcQuerySelect $query, $class ) { $def = $this->manager->fetchDefinition( $class ); // propagate exception try { $stmt = $query->prepare(); $stmt->execute(); $rows = $stmt->fetchAll( PDO::FETCH_ASSOC ); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } // 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 result of the query $query as an object iterator. * * This method is similar to find() but returns an iterator * instead of a list of objects. This is useful if you are going * to loop over the objects and just need them one at the time. * Because you only instantiate one object is is faster than find(). * * @throws ezcPersistentDefinitionNotFoundException if there is no such persistent class. * @throws ezcPersistentQueryException if the find query failed * @param ezcQuerySelect $query * @return Iterator */ public function findIterator( ezcQuerySelect $query, $class ) { $def = $this->manager->fetchDefinition( $class ); // propagate exception try { $stmt = $query->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } return new ezcPersistentFindIterator( $stmt, $def ); } /** * Returns the persistent object of class $class with id $id. * * @throws ezcPersistentException if the object is not available. * @throws ezcPersistentException if there is no such persistent class. * @param string $class * @param int $id * @return object */ public function load( $class, $id ) { $def = $this->manager->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 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 $pObject. * * The class of the persistent object to load is determined by the class * of $pObject. * * @throws ezcPersistentException if the object is not available. * @throws ezcPersistentDefinitionNotFoundException if $pObject is not of a valid persistent object type. * @throws ezcPersistentQueryException if the find query failed * @param object $pObject * @param int $id * @return void */ public function loadIntoObject( $pObject, $id ) { if ( !is_numeric( $id ) ) { throw new ezcPersistentQueryException( "The parameter 'id' was not a valid integer." ); } $def = $this->manager->fetchDefinition( get_class( $pObject ) ); // propagate exception $q = $this->db->createSelectQuery(); $q->select( '*' )->from( $def->table ) ->where( $q->expr->eq( $def->idProperty->columnName, $q->bindValue( $id ) ) ); try { $stmt = $q->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } $row = $stmt->fetch( PDO::FETCH_ASSOC ); 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" ); } $pObject->setState( $state ); } else { $class = get_class( $pObject ); throw new ezcPersistentQueryException( "No such object $class with id $id." ); } } /** * Syncronizes the contents of $pObject with those in the database. * * Note that calling this method is equavalent with calling * loadIntoObject on $pObject with the id of $pObject. Any * changes made to $pObject prior to calling refresh() will be discarded. * * @throws ezcPersistentException if $pObject is not of a valid persistent object type. * @throws ezcPersistentException if $pObject is not persistent already * @throws ezcPersistentException if the select query failed. * @param object $pObject * @return void */ public function refresh( $pObject ) { $def = $this->manager->fetchDefinition( get_class( $pObject ) ); // propagate exception $state = $pObject->getState(); $idValue = $state[$def->idProperty->propertyName]; if ( $idValue !== null ) { $this->loadIntoObject( $pObject, $idValue ); } else { $class = get_class( $pObject ); throw new ezcPersistentObjectNotPersistentException( $class ); } } /** * Saves the new persistent object $pObject to the database using an INSERT INTO query. * * The correct ID is set to $pObject. * * @throws ezcPersistentException if $pObject is not of a valid persistent object type. * @throws ezcPersistentException if $pObject is already stored to the database. * @throws ezcPersistentException if it was not possible to generate a unique identifier for the new object * @throws ezcPersistentException if the insert query failed. * @param object $pObject * @return void */ public function save( $pObject ) { $def = $this->manager->fetchDefinition( get_class( $pObject ) );// propagate exception $state = $pObject->getState(); $idValue = $state[$def->idProperty->propertyName]; // check that this object is stored to db already if ( $idValue !== null ) { $class = get_class( $pObject ); throw new ezcPersistentObjectAlreadyPersistentException( $class ); } // set up and execute the query $q = $this->db->createInsertQuery(); $q->insertInto( $def->table ); foreach ( $state as $name => $value ) { if ( $name != $def->idProperty->propertyName ) // skip the id field { // set each of the properties $q->set( $def->properties[$name]->columnName, $q->bindValue( $value ) ); } } // fetch the id generator $idGenerator = null; if ( class_exists( $def->idProperty->generator->class ) ) { $idGenerator = new $def->idProperty->generator->class; if ( !( $idGenerator instanceof ezcPersistentIdentifierGenerator ) ) { throw new ezcPersistentIdentifierGenerationException( get_class( $pObject ), "Could not initialize identifier generator: ". "{$def->idProperty->generator->class} ." ); } } $this->db->beginTransaction(); // let presave id generator do its work $idGenerator->preSave( $def, $this->db, $q ); // execute the insert query try { $stmt = $q->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentObjectException( "The insert query failed." ); } // fetch the newly created id, and set it to the object $id = $idGenerator->postSave( $def, $this->db ); if ( $id === null ) { $this->db->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->db->commit(); $state[$def->idProperty->propertyName] = $id; $pObject->setState( $state ); } /** * Saves or update the persistent object $pObject to the database. * * If the object is a new object an INSERT INTO query will be executed. If the * object is persistent already it will be updated with an UPDATE query. * * @throws ezcPersistentException if $pObject is not of a valid persistent object type. * @throws ezcPersistentException if any of the definition requirements are not met. * @throws ezcPersistentException if the insert or update query failed. * @param object $pObject * @return void */ public function saveOrUpdate( $pObject ) { $def = $this->manager->fetchDefinition( get_class( $pObject ) );// propagate exception $state = $pObject->getState(); $idValue = $state[$def->idProperty->propertyName]; if ( $idValue === null ) { $this->save( $pObject ); } else { $this->update( $pObject ); } } /** * Saves the new persistent object $pObject to the database using an UPDATE query. * * @throws ezcPersistentDefinitionNotFoundException if $pObject is not of a valid persistent object type. * @throws ezcPersistentObjectNotPersistentException if $pObject is not stored in the database already. * @throws ezcPersistentQueryException * @param object $pObject * @return void */ public function update( $pObject ) { $def = $this->manager->fetchDefinition( get_class( $pObject ) ); // propagate exception $state = $pObject->getState(); $idValue = $state[$def->idProperty->propertyName]; // check that this object is stored to db already if ( $idValue < 1 ) { throw new ezcPersistentObjectNotPersistentException( get_class( $pObject ) ); } // set up and execute the query $q = $this->db->createUpdateQuery(); $q->update( $def->table ); foreach ( $state as $name => $value ) { if ( $name != $def->idProperty->propertyName ) // skip the id field { // set each of the properties $q->set( $def->properties[$name]->columnName, $q->bindValue( $value ) ); } } $q->where( $q->expr->eq( $def->idProperty->columnName, $q->bindValue( $idValue ) ) ); try { $stmt = $q->prepare(); $stmt->execute(); } catch ( PDOException $e ) { throw new ezcPersistentQueryException( $e->getMessage() ); } } // ignore this for now /* * Goes through the requirements of the array $state and and check that the requirements * set in $def are met. * * Currently this method checks if a field marked as required is set to null. * If this is the case this method will replace it with the default value if present. * If it is not possible to meet the requirements an exception is thrown. * * @param array $row * @param ezcPersistentDefinition $def * @return void */ // protected function meetRequirements( array &$state, ezcPersistentObjectDefinition $def ) // { // foreach ( $state as $key => $value ) // } /** * Returns a hash map between property and column name for the given definition $def. * * The alias map can be used with the query classes. * * @param ezcPersistentObjectDefinition $def * @return array(string=>string) */ private function generateAliasMap( ezcPersistentObjectDefinition $def ) { $table = array(); foreach ( $def->properties as $prop ) { $table[$prop->propertyName] = $prop->columnName; } $table[$def->class] = $def->table; return $table; } } ?>