2006-09-20 13:46:52 +04:00
< ? php
/*
* $Id $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL . For more information , see
* < http :// www . phpdoctrine . com >.
*/
2006-11-16 15:46:23 +03:00
Doctrine :: autoload ( 'Doctrine_Connection_Module' );
2006-09-20 13:46:52 +04:00
/**
2006-09-20 15:29:35 +04:00
* Doctrine_Connection_UnitOfWork
2006-09-20 13:46:52 +04:00
*
2006-11-16 15:46:23 +03:00
* @ package Doctrine
* @ license http :// www . opensource . org / licenses / lgpl - license . php LGPL
* @ category Object Relational Mapping
* @ link www . phpdoctrine . com
* @ since 1.0
* @ version $Revision $
* @ author Konsta Vesterinen < kvesteri @ cc . hut . fi >
*/
class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implements IteratorAggregate , Countable {
2006-09-20 13:46:52 +04:00
/**
* buildFlushTree
* builds a flush tree that is used in transactions
2006-10-31 02:00:09 +03:00
*
* The returned array has all the initialized components in
* 'correct' order . Basically this means that the records of those
* components can be saved safely in the order specified by the returned array .
2006-09-20 13:46:52 +04:00
*
2006-10-31 02:00:09 +03:00
* @ param array $tables
2006-09-20 13:46:52 +04:00
* @ return array
*/
public function buildFlushTree ( array $tables ) {
$tree = array ();
foreach ( $tables as $k => $table ) {
$k = $k . $table ;
if ( ! ( $table instanceof Doctrine_Table ))
$table = $this -> conn -> getTable ( $table );
$nm = $table -> getComponentName ();
$index = array_search ( $nm , $tree );
if ( $index === false ) {
$tree [] = $nm ;
$index = max ( array_keys ( $tree ));
}
$rels = $table -> getRelations ();
// group relations
foreach ( $rels as $key => $rel ) {
2006-09-29 00:57:39 +04:00
if ( $rel instanceof Doctrine_Relation_ForeignKey ) {
2006-09-20 13:46:52 +04:00
unset ( $rels [ $key ]);
array_unshift ( $rels , $rel );
}
}
foreach ( $rels as $rel ) {
$name = $rel -> getTable () -> getComponentName ();
$index2 = array_search ( $name , $tree );
$type = $rel -> getType ();
// skip self-referenced relations
if ( $name === $nm )
continue ;
2006-09-29 00:57:39 +04:00
if ( $rel instanceof Doctrine_Relation_ForeignKey ) {
2006-09-20 13:46:52 +04:00
if ( $index2 !== false ) {
if ( $index2 >= $index )
continue ;
unset ( $tree [ $index ]);
array_splice ( $tree , $index2 , 0 , $nm );
$index = $index2 ;
} else {
$tree [] = $name ;
}
2006-09-29 00:57:39 +04:00
} elseif ( $rel instanceof Doctrine_Relation_LocalKey ) {
2006-09-20 13:46:52 +04:00
if ( $index2 !== false ) {
if ( $index2 <= $index )
continue ;
unset ( $tree [ $index2 ]);
array_splice ( $tree , $index , 0 , $name );
} else {
array_unshift ( $tree , $name );
$index ++ ;
}
2006-09-29 00:57:39 +04:00
} elseif ( $rel instanceof Doctrine_Relation_Association ) {
2006-09-20 13:46:52 +04:00
$t = $rel -> getAssociationFactory ();
$n = $t -> getComponentName ();
if ( $index2 !== false )
unset ( $tree [ $index2 ]);
array_splice ( $tree , $index , 0 , $name );
$index ++ ;
$index3 = array_search ( $n , $tree );
if ( $index3 !== false ) {
if ( $index3 >= $index )
continue ;
unset ( $tree [ $index ]);
array_splice ( $tree , $index3 , 0 , $n );
$index = $index2 ;
} else {
$tree [] = $n ;
}
}
}
}
return array_values ( $tree );
}
2006-10-31 02:00:09 +03:00
/**
* saveRelated
* saves all related records to $record
*
2006-10-31 13:42:46 +03:00
* @ throws PDOException if something went wrong at database level
2006-10-31 02:00:09 +03:00
* @ param Doctrine_Record $record
*/
public function saveRelated ( Doctrine_Record $record ) {
$saveLater = array ();
foreach ( $record -> getReferences () as $k => $v ) {
$fk = $record -> getTable () -> getRelation ( $k );
if ( $fk instanceof Doctrine_Relation_ForeignKey ||
$fk instanceof Doctrine_Relation_LocalKey ) {
if ( $fk -> isComposite ()) {
$local = $fk -> getLocal ();
$foreign = $fk -> getForeign ();
if ( $record -> getTable () -> hasPrimaryKey ( $fk -> getLocal ())) {
if ( ! $record -> exists ())
$saveLater [ $k ] = $fk ;
else
$v -> save ();
} else {
// ONE-TO-ONE relationship
$obj = $record -> get ( $fk -> getTable () -> getComponentName ());
if ( $obj -> getState () != Doctrine_Record :: STATE_TCLEAN )
$obj -> save ();
}
}
} elseif ( $fk instanceof Doctrine_Relation_Association ) {
$v -> save ();
}
}
return $saveLater ;
}
/**
* saveAssociations
*
* this method takes a diff of one - to - many / many - to - many original and
* current collections and applies the changes
*
* for example if original many - to - many related collection has records with
* primary keys 1 , 2 and 3 and the new collection has records with primary keys
* 3 , 4 and 5 , this method would first destroy the associations to 1 and 2 and then
* save new associations to 4 and 5
*
2006-10-31 13:42:46 +03:00
* @ throws PDOException if something went wrong at database level
2006-10-31 02:00:09 +03:00
* @ param Doctrine_Record $record
* @ return void
*/
public function saveAssociations ( Doctrine_Record $record ) {
foreach ( $record -> getTable () -> getRelations () as $rel ) {
$table = $rel -> getTable ();
$alias = $rel -> getAlias ();
2006-09-20 13:46:52 +04:00
2006-10-31 02:00:09 +03:00
$rel -> processDiff ( $record );
}
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
2006-10-31 13:42:46 +03:00
* @ throws PDOException if something went wrong at database level
2006-10-31 02:00:09 +03:00
* @ return void
*/
public function deleteComposites ( Doctrine_Record $record ) {
foreach ( $record -> getTable () -> getRelations () as $fk ) {
switch ( $fk -> getType ()) :
case Doctrine_Relation :: ONE_COMPOSITE :
case Doctrine_Relation :: MANY_COMPOSITE :
$obj = $record -> get ( $fk -> getAlias ());
$obj -> delete ();
break ;
endswitch ;
}
}
2006-10-31 13:42:46 +03:00
/**
* saveAll
* persists all the pending records from all tables
*
* @ throws PDOException if something went wrong at database level
* @ return void
*/
public function saveAll () {
// get the flush tree
$tree = $this -> buildFlushTree ( $this -> conn -> getTables ());
// save all records
foreach ( $tree as $name ) {
$table = $this -> conn -> getTable ( $name );
foreach ( $table -> getRepository () as $record ) {
$this -> conn -> save ( $record );
}
}
// save all associations
foreach ( $tree as $name ) {
$table = $this -> conn -> getTable ( $name );
foreach ( $table -> getRepository () as $record ) {
$this -> saveAssociations ( $record );
}
}
}
2006-11-30 00:09:02 +03:00
/**
* updates the given record
*
* @ param Doctrine_Record $record
* @ return boolean
*/
public function update ( Doctrine_Record $record ) {
$record -> getTable () -> getAttribute ( Doctrine :: ATTR_LISTENER ) -> onPreUpdate ( $record );
$array = $record -> getPrepared ();
if ( empty ( $array ))
return false ;
$set = array ();
foreach ( $array as $name => $value ) :
$set [] = $name . " = ? " ;
if ( $value instanceof Doctrine_Record ) {
switch ( $value -> getState ()) :
case Doctrine_Record :: STATE_TCLEAN :
case Doctrine_Record :: STATE_TDIRTY :
$record -> save ();
default :
$array [ $name ] = $value -> getIncremented ();
$record -> set ( $name , $value -> getIncremented ());
endswitch ;
}
endforeach ;
$params = array_values ( $array );
$id = $record -> obtainIdentifier ();
if ( ! is_array ( $id ))
$id = array ( $id );
$id = array_values ( $id );
$params = array_merge ( $params , $id );
$sql = " UPDATE " . $record -> getTable () -> getTableName () . " SET " . implode ( " , " , $set ) . " WHERE " . implode ( " = ? AND " , $record -> getTable () -> getPrimaryKeys ()) . " = ? " ;
$stmt = $this -> conn -> getDBH () -> prepare ( $sql );
$stmt -> execute ( $params );
$record -> assignIdentifier ( true );
$record -> getTable () -> getAttribute ( Doctrine :: ATTR_LISTENER ) -> onUpdate ( $record );
return true ;
}
/**
* inserts a record into database
*
* @ param Doctrine_Record $record record to be inserted
* @ return boolean
*/
public function insert ( Doctrine_Record $record ) {
// listen the onPreInsert event
$record -> getTable () -> getAttribute ( Doctrine :: ATTR_LISTENER ) -> onPreInsert ( $record );
$array = $record -> getPrepared ();
if ( empty ( $array ))
return false ;
$table = $record -> getTable ();
$keys = $table -> getPrimaryKeys ();
$seq = $record -> getTable () -> getSequenceName ();
if ( ! empty ( $seq )) {
$id = $this -> nextId ( $seq );
$name = $record -> getTable () -> getIdentifier ();
$array [ $name ] = $id ;
}
$this -> conn -> insert ( $table -> getTableName (), $array );
if ( count ( $keys ) == 1 && $keys [ 0 ] == $table -> getIdentifier ()) {
$id = $this -> conn -> getDBH () -> lastInsertID ();
if ( ! $id )
$id = $table -> getMaxIdentifier ();
$record -> assignIdentifier ( $id );
} else
$record -> assignIdentifier ( true );
// listen the onInsert event
$table -> getAttribute ( Doctrine :: ATTR_LISTENER ) -> onInsert ( $record );
return true ;
}
2006-09-20 13:46:52 +04:00
public function getIterator () { }
public function count () { }
}