Refactored Doctrine_Connection and Doctrine_Record, fixes #212
This commit is contained in:
parent
d8dddffcfd
commit
7ef869ee40
@ -645,7 +645,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
}
|
||||
/**
|
||||
* save
|
||||
* saves all records
|
||||
* saves all records of this collection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -653,19 +653,34 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
if ($conn == null) {
|
||||
$conn = $this->table->getConnection();
|
||||
}
|
||||
$conn->saveCollection($this);
|
||||
$conn->beginTransaction();
|
||||
|
||||
foreach($this as $key => $record):
|
||||
$record->save();
|
||||
endforeach;
|
||||
|
||||
$conn->commit();
|
||||
}
|
||||
/**
|
||||
* single shot delete
|
||||
* deletes all records from this collection
|
||||
* uses only one database query to perform this operation
|
||||
* and uses only one database query to perform this operation
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete(Doctrine_Connection $conn = null) {
|
||||
if ($conn == null) {
|
||||
$conn = $this->table->getConnection();
|
||||
}
|
||||
$ids = $conn->deleteCollection($this);
|
||||
|
||||
$conn->beginTransaction();
|
||||
|
||||
foreach($this as $key => $record) {
|
||||
$record->delete();
|
||||
}
|
||||
|
||||
$conn->commit();
|
||||
|
||||
$this->data = array();
|
||||
}
|
||||
/**
|
||||
|
@ -48,13 +48,30 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
* @var Doctrine_DataDict $dataDict
|
||||
*/
|
||||
private $dataDict;
|
||||
|
||||
|
||||
private static $availibleDrivers = array(
|
||||
"Mysql",
|
||||
"Pgsql",
|
||||
"Oracle",
|
||||
"Informix",
|
||||
"Mssql",
|
||||
"Sqlite",
|
||||
"Firebird"
|
||||
);
|
||||
private static $driverMap = array('oracle' => 'oci8',
|
||||
'postgres' => 'pgsql',
|
||||
'oci' => 'oci8',
|
||||
'sqlite2' => 'sqlite',
|
||||
'sqlite3' => 'sqlite');
|
||||
|
||||
/**
|
||||
* the constructor
|
||||
*
|
||||
* @param Doctrine_Manager $manager the manager object
|
||||
* @param PDO $pdo the database handler
|
||||
*/
|
||||
public function __construct(Doctrine_Manager $manager,PDO $pdo) {
|
||||
public function __construct(Doctrine_Manager $manager, PDO $pdo) {
|
||||
$this->dbh = $pdo;
|
||||
|
||||
$this->transaction = new Doctrine_Connection_Transaction($this);
|
||||
@ -112,6 +129,13 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
public function getDBH() {
|
||||
return $this->dbh;
|
||||
}
|
||||
/**
|
||||
* converts given driver name
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
public function driverName($name) {
|
||||
}
|
||||
/**
|
||||
* returns a datadict object
|
||||
*
|
||||
@ -202,7 +226,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
*/
|
||||
public function select($query,$limit = 0,$offset = 0) {
|
||||
if($limit > 0 || $offset > 0)
|
||||
$query = $this->modifyLimitQuery($query,$limit,$offset);
|
||||
$query = $this->modifyLimitQuery($query, $limit, $offset);
|
||||
|
||||
return $this->dbh->query($query);
|
||||
}
|
||||
@ -332,7 +356,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
foreach($tree as $name) {
|
||||
$table = $this->tables[$name];
|
||||
foreach($table->getRepository() as $record) {
|
||||
$record->saveAssociations();
|
||||
$this->unitOfWork->saveAssociations($record);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -409,60 +433,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
public function rollback() {
|
||||
$this->transaction->rollback();
|
||||
}
|
||||
/**
|
||||
* returns maximum identifier values
|
||||
*
|
||||
* @param array $names an array of component names
|
||||
* @return array
|
||||
*/
|
||||
public function getMaximumValues(array $names) {
|
||||
$values = array();
|
||||
foreach($names as $name) {
|
||||
$table = $this->tables[$name];
|
||||
$keys = $table->getPrimaryKeys();
|
||||
$tablename = $table->getTableName();
|
||||
|
||||
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
|
||||
// record uses auto_increment column
|
||||
|
||||
$sql = "SELECT MAX(".$table->getIdentifier().") FROM ".$tablename;
|
||||
$stmt = $this->dbh->query($sql);
|
||||
$data = $stmt->fetch(PDO::FETCH_NUM);
|
||||
$values[$tablename] = $data[0];
|
||||
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
/**
|
||||
* saves a collection
|
||||
*
|
||||
* @param Doctrine_Collection $coll
|
||||
* @return void
|
||||
*/
|
||||
public function saveCollection(Doctrine_Collection $coll) {
|
||||
$this->beginTransaction();
|
||||
|
||||
foreach($coll as $key=>$record):
|
||||
$record->save();
|
||||
endforeach;
|
||||
|
||||
$this->commit();
|
||||
}
|
||||
/**
|
||||
* deletes all records from collection
|
||||
*
|
||||
* @param Doctrine_Collection $coll
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCollection(Doctrine_Collection $coll) {
|
||||
$this->beginTransaction();
|
||||
foreach($coll as $k=>$record) {
|
||||
$record->delete();
|
||||
}
|
||||
$this->commit();
|
||||
}
|
||||
/**
|
||||
* saves the given record
|
||||
*
|
||||
@ -489,125 +459,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
|
||||
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
|
||||
}
|
||||
/**
|
||||
* saves all related records to $record
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
/**
|
||||
* deletes all related composites
|
||||
* this method is always called internally when a record is deleted
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final 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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* saveAssociations
|
||||
* save the associations of many-to-many relations
|
||||
* this method also deletes associations that do not exist anymore
|
||||
* @return void
|
||||
*/
|
||||
final public function saveAssociations(Doctrine_Record $record) {
|
||||
foreach($record->getTable()->table->getRelations() as $rel):
|
||||
$table = $rel->getTable();
|
||||
$name = $table->getComponentName();
|
||||
$alias = $this->table->getAlias($name);
|
||||
|
||||
if($rel instanceof Doctrine_Relation_Association) {
|
||||
|
||||
$asf = $rel->getAssociationFactory();
|
||||
|
||||
if($record->hasReference($alias)) {
|
||||
|
||||
$new = $record->getReference($alias);
|
||||
|
||||
if( ! $this->hasOriginalsFor($alias))
|
||||
$record->loadReference($alias);
|
||||
|
||||
|
||||
$operations = Doctrine_Relation::getDeleteOperations($this->originals[$alias],$new);
|
||||
|
||||
foreach($operations as $r) {
|
||||
$query = "DELETE FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." = ?"
|
||||
." AND ".$fk->getLocal()." = ?";
|
||||
$this->table->getConnection()->execute($query, array($r->getIncremented(),$record->getIncremented()));
|
||||
}
|
||||
|
||||
$operations = Doctrine_Relation::getInsertOperations($record->obtainOriginals($alias),$new);
|
||||
foreach($operations as $r) {
|
||||
$reldao = $asf->create();
|
||||
$reldao->set($fk->getForeign(),$r);
|
||||
$reldao->set($fk->getLocal(),$this);
|
||||
$reldao->save();
|
||||
|
||||
}
|
||||
$record->assignOriginals($alias, clone $this->references[$alias]);
|
||||
}
|
||||
} elseif($fk instanceof Doctrine_Relation_ForeignKey ||
|
||||
$fk instanceof Doctrine_Relation_LocalKey) {
|
||||
|
||||
if($fk->isOneToOne()) {
|
||||
if($record->obtainOriginals($alias) && $record->obtainOriginals($alias)->obtainIdentifier() != $this->references[$alias]->obtainIdentifier())
|
||||
$record->obtainOriginals($alias)->delete();
|
||||
} else {
|
||||
if(isset($this->references[$alias])) {
|
||||
$new = $this->references[$alias];
|
||||
|
||||
if( ! isset($this->originals[$alias]))
|
||||
$record->loadReference($alias);
|
||||
|
||||
$operations = Doctrine_Relation::getDeleteOperations($this->originals[$alias], $new);
|
||||
|
||||
foreach($operations as $r) {
|
||||
$r->delete();
|
||||
}
|
||||
|
||||
$record->assignOriginals($alias, clone $this->references[$alias]);
|
||||
}
|
||||
}
|
||||
}
|
||||
endforeach;
|
||||
}
|
||||
/**
|
||||
* deletes this data access object and all the related composites
|
||||
* this operation is isolated by a transaction
|
||||
@ -616,7 +467,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
*
|
||||
* @return boolean true on success, false on failure
|
||||
*/
|
||||
final public function delete(Doctrine_Record $record) {
|
||||
public function delete(Doctrine_Record $record) {
|
||||
if( ! $record->exists())
|
||||
return false;
|
||||
|
||||
@ -624,7 +475,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
|
||||
|
||||
$record->getTable()->getListener()->onPreDelete($record);
|
||||
|
||||
$this->deleteComposites($record);
|
||||
$this->unitOfWork->deleteComposites($record);
|
||||
|
||||
$this->transaction->addDelete($record);
|
||||
|
||||
|
@ -35,6 +35,28 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common {
|
||||
public function __construct(Doctrine_Manager $manager,PDO $pdo) {
|
||||
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
$this->setAttribute(Doctrine::ATTR_QUERY_LIMIT, Doctrine::LIMIT_ROWS);
|
||||
|
||||
$this->supported = array(
|
||||
'sequences' => 'emulated',
|
||||
'indexes' => true,
|
||||
'affected_rows' => true,
|
||||
'transactions' => true,
|
||||
'savepoints' => false,
|
||||
'summary_functions' => true,
|
||||
'order_by_text' => true,
|
||||
'current_id' => 'emulated',
|
||||
'limit_queries' => true,
|
||||
'LOBs' => true,
|
||||
'replace' => true,
|
||||
'sub_selects' => true,
|
||||
'auto_increment' => true,
|
||||
'primary_key' => true,
|
||||
'result_introspection' => true,
|
||||
'prepared_statements' => 'emulated',
|
||||
'identifier_quoting' => true,
|
||||
'pattern_escaping' => true
|
||||
);
|
||||
|
||||
parent::__construct($manager,$pdo);
|
||||
}
|
||||
/**
|
||||
|
@ -44,7 +44,12 @@ class Doctrine_Connection_UnitOfWork implements IteratorAggregate, Countable {
|
||||
/**
|
||||
* buildFlushTree
|
||||
* builds a flush tree that is used in transactions
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $tables
|
||||
* @return array
|
||||
*/
|
||||
public function buildFlushTree(array $tables) {
|
||||
@ -133,7 +138,81 @@ class Doctrine_Connection_UnitOfWork implements IteratorAggregate, Countable {
|
||||
}
|
||||
return array_values($tree);
|
||||
}
|
||||
/**
|
||||
* saveRelated
|
||||
* saves all related records to $record
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @param Doctrine_Record $record
|
||||
* @return void
|
||||
*/
|
||||
public function saveAssociations(Doctrine_Record $record) {
|
||||
foreach($record->getTable()->getRelations() as $rel) {
|
||||
$table = $rel->getTable();
|
||||
$alias = $rel->getAlias();
|
||||
|
||||
$rel->processDiff($record);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* deletes all related composites
|
||||
* this method is always called internally when a record is deleted
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
public function getIterator() { }
|
||||
|
||||
public function count() { }
|
||||
|
@ -598,25 +598,26 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* DQL PARSER
|
||||
* parses a DQL query
|
||||
* first splits the query in parts and then uses individual
|
||||
* parsers for each part
|
||||
* splitQuery
|
||||
* splits the given dql query into an array where keys
|
||||
* represent different query part names and values are
|
||||
* arrays splitted using sqlExplode method
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* parameter:
|
||||
* $query = "SELECT u.* FROM User u WHERE u.name LIKE ?"
|
||||
* returns:
|
||||
* array('select' => array('u.*'),
|
||||
* 'from' => array('User', 'u'),
|
||||
* 'where' => array('u.name', 'LIKE', '?'))
|
||||
*
|
||||
* @param string $query DQL query
|
||||
* @param boolean $clear whether or not to clear the aliases
|
||||
* @throws Doctrine_Query_Exception if some generic parsing error occurs
|
||||
* @return Doctrine_Query
|
||||
* @return array an array containing the query string parts
|
||||
*/
|
||||
public function parseQuery($query, $clear = true) {
|
||||
if($clear)
|
||||
$this->clear();
|
||||
|
||||
$query = trim($query);
|
||||
$query = str_replace("\n"," ",$query);
|
||||
$query = str_replace("\r"," ",$query);
|
||||
|
||||
$e = self::sqlExplode($query," ","(",")");
|
||||
public function splitQuery($query) {
|
||||
$e = self::sqlExplode($query, ' ');
|
||||
|
||||
foreach($e as $k=>$part) {
|
||||
$part = trim($part);
|
||||
@ -651,6 +652,28 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
$parts[$p][] = $part;
|
||||
}
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
/**
|
||||
* DQL PARSER
|
||||
* parses a DQL query
|
||||
* first splits the query in parts and then uses individual
|
||||
* parsers for each part
|
||||
*
|
||||
* @param string $query DQL query
|
||||
* @param boolean $clear whether or not to clear the aliases
|
||||
* @throws Doctrine_Query_Exception if some generic parsing error occurs
|
||||
* @return Doctrine_Query
|
||||
*/
|
||||
public function parseQuery($query, $clear = true) {
|
||||
if($clear)
|
||||
$this->clear();
|
||||
|
||||
$query = trim($query);
|
||||
$query = str_replace("\n"," ",$query);
|
||||
$query = str_replace("\r"," ",$query);
|
||||
|
||||
$parts = $this->splitQuery($query);
|
||||
|
||||
foreach($parts as $k => $part) {
|
||||
$part = implode(" ",$part);
|
||||
@ -753,11 +776,18 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
}
|
||||
/**
|
||||
* bracketExplode
|
||||
* usage:
|
||||
* $str = (age < 20 AND age > 18) AND email LIKE 'John@example.com'
|
||||
* now exploding $str with parameters $d = ' AND ', $e1 = '(' and $e2 = ')'
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* parameters:
|
||||
* $str = (age < 20 AND age > 18) AND email LIKE 'John@example.com'
|
||||
* $d = ' AND '
|
||||
* $e1 = '('
|
||||
* $e2 = ')'
|
||||
*
|
||||
* would return an array:
|
||||
* array("(age < 20 AND age > 18)", "email LIKE 'John@example.com'")
|
||||
* array("(age < 20 AND age > 18)",
|
||||
* "email LIKE 'John@example.com'")
|
||||
*
|
||||
* @param string $str
|
||||
* @param string $d the delimeter which explodes the string
|
||||
@ -765,7 +795,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
* @param string $e2 the second bracket, usually ')'
|
||||
*
|
||||
*/
|
||||
public static function bracketExplode($str,$d,$e1 = '(',$e2 = ')') {
|
||||
public static function bracketExplode($str, $d = ' ', $e1 = '(', $e2 = ')') {
|
||||
if(is_array($d)) {
|
||||
$a = preg_split('/('.implode('|', $d).')/', $str);
|
||||
$d = stripslashes($d[0]);
|
||||
@ -777,13 +807,13 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
foreach($a as $key=>$val) {
|
||||
if (empty($term[$i])) {
|
||||
$term[$i] = trim($val);
|
||||
$s1 = substr_count($term[$i],"$e1");
|
||||
$s2 = substr_count($term[$i],"$e2");
|
||||
$s1 = substr_count($term[$i], "$e1");
|
||||
$s2 = substr_count($term[$i], "$e2");
|
||||
if($s1 == $s2) $i++;
|
||||
} else {
|
||||
$term[$i] .= "$d".trim($val);
|
||||
$c1 = substr_count($term[$i],"$e1");
|
||||
$c2 = substr_count($term[$i],"$e2");
|
||||
$c1 = substr_count($term[$i], "$e1");
|
||||
$c2 = substr_count($term[$i], "$e2");
|
||||
if($c1 == $c2) $i++;
|
||||
}
|
||||
}
|
||||
@ -795,6 +825,21 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
* explodes a string into array using custom brackets and
|
||||
* quote delimeters
|
||||
*
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* parameters:
|
||||
* $str = "(age < 20 AND age > 18) AND name LIKE 'John Doe'"
|
||||
* $d = ' '
|
||||
* $e1 = '('
|
||||
* $e2 = ')'
|
||||
*
|
||||
* would return an array:
|
||||
* array('(age < 20 AND age > 18)',
|
||||
* 'name',
|
||||
* 'LIKE',
|
||||
* 'John Doe')
|
||||
*
|
||||
* @param string $str
|
||||
* @param string $d the delimeter which explodes the string
|
||||
* @param string $e1 the first bracket, usually '('
|
||||
@ -802,7 +847,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function sqlExplode($str,$d = " ",$e1 = '(',$e2 = ')') {
|
||||
public static function sqlExplode($str, $d = ' ', $e1 = '(', $e2 = ')') {
|
||||
if(is_array($d)) {
|
||||
$str = preg_split('/('.implode('|', $d).')/', $str);
|
||||
$d = stripslashes($d[0]);
|
||||
|
@ -857,8 +857,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
$conn = $this->_table->getConnection();
|
||||
}
|
||||
$conn->beginTransaction();
|
||||
|
||||
$saveLater = $conn->saveRelated($this);
|
||||
|
||||
|
||||
$saveLater = $conn->getUnitOfWork()->saveRelated($this);
|
||||
|
||||
if ($this->isValid()) {
|
||||
$conn->save($this);
|
||||
@ -878,7 +879,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
|
||||
// save the MANY-TO-MANY associations
|
||||
|
||||
$this->saveAssociations();
|
||||
$conn->getUnitOfWork()->saveAssociations($this);
|
||||
//$this->saveAssociations();
|
||||
|
||||
$conn->commit();
|
||||
}
|
||||
@ -1012,99 +1014,18 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
public function getIterator() {
|
||||
return new Doctrine_Record_Iterator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* getOriginals
|
||||
* returns an original collection of related component
|
||||
*
|
||||
* @return Doctrine_Collection|false
|
||||
*/
|
||||
public function obtainOriginals($name) {
|
||||
if(isset($this->originals[$name]))
|
||||
return $this->originals[$name];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveAssociations
|
||||
*
|
||||
* save the associations of many-to-many relations
|
||||
* this method also deletes associations that do not exist anymore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function saveAssociations() {
|
||||
foreach($this->_table->getRelations() as $fk) {
|
||||
$table = $fk->getTable();
|
||||
$name = $table->getComponentName();
|
||||
$alias = $this->_table->getAlias($name);
|
||||
|
||||
if($fk instanceof Doctrine_Relation_Association) {
|
||||
switch($fk->getType()):
|
||||
case Doctrine_Relation::MANY_AGGREGATE:
|
||||
$asf = $fk->getAssociationFactory();
|
||||
|
||||
if(isset($this->references[$alias])) {
|
||||
|
||||
$new = $this->references[$alias];
|
||||
|
||||
if( ! isset($this->originals[$alias])) {
|
||||
$this->loadReference($alias);
|
||||
}
|
||||
|
||||
$r = Doctrine_Relation::getDeleteOperations($this->originals[$alias],$new);
|
||||
|
||||
foreach($r as $record) {
|
||||
$query = "DELETE FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." = ?"
|
||||
." AND ".$fk->getLocal()." = ?";
|
||||
$this->_table->getConnection()->execute($query, array($record->getIncremented(),$this->getIncremented()));
|
||||
}
|
||||
|
||||
$r = Doctrine_Relation::getInsertOperations($this->originals[$alias],$new);
|
||||
foreach($r as $record) {
|
||||
$reldao = $asf->create();
|
||||
$reldao->set($fk->getForeign(),$record);
|
||||
$reldao->set($fk->getLocal(),$this);
|
||||
$reldao->save();
|
||||
|
||||
}
|
||||
$this->originals[$alias] = clone $this->references[$alias];
|
||||
}
|
||||
break;
|
||||
endswitch;
|
||||
} elseif($fk instanceof Doctrine_Relation_ForeignKey ||
|
||||
$fk instanceof Doctrine_Relation_LocalKey) {
|
||||
|
||||
if($fk->isOneToOne()) {
|
||||
if(isset($this->originals[$alias]) && $this->originals[$alias]->obtainIdentifier() != $this->references[$alias]->obtainIdentifier())
|
||||
$this->originals[$alias]->delete();
|
||||
|
||||
} else {
|
||||
if(isset($this->references[$alias])) {
|
||||
$new = $this->references[$alias];
|
||||
|
||||
if( ! isset($this->originals[$alias]))
|
||||
$this->loadReference($alias);
|
||||
|
||||
$r = Doctrine_Relation::getDeleteOperations($this->originals[$alias], $new);
|
||||
|
||||
foreach($r as $record) {
|
||||
$record->delete();
|
||||
}
|
||||
|
||||
$this->originals[$alias] = clone $this->references[$alias];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* getOriginals
|
||||
* returns an original collection of related component
|
||||
*
|
||||
* @return Doctrine_Collection
|
||||
*/
|
||||
final public function getOriginals($name) {
|
||||
if( ! isset($this->originals[$name]))
|
||||
throw new InvalidKeyException();
|
||||
|
||||
return $this->originals[$name];
|
||||
}
|
||||
/**
|
||||
* deletes this data access object and all the related composites
|
||||
* this operation is isolated by a transaction
|
||||
|
@ -52,6 +52,45 @@ class Doctrine_Relation_Association extends Doctrine_Relation {
|
||||
public function getAssociationFactory() {
|
||||
return $this->associationTable;
|
||||
}
|
||||
/**
|
||||
* processDiff
|
||||
*
|
||||
* @param Doctrine_Record
|
||||
*/
|
||||
public function processDiff(Doctrine_Record $record) {
|
||||
$asf = $this->getAssociationFactory();
|
||||
$alias = $this->getAlias();
|
||||
|
||||
if($record->hasReference($alias)) {
|
||||
|
||||
$new = $record->obtainReference($alias);
|
||||
|
||||
if( ! $record->obtainOriginals($alias))
|
||||
$record->loadReference($alias);
|
||||
|
||||
|
||||
$operations = Doctrine_Relation::getDeleteOperations($record->obtainOriginals($alias), $new);
|
||||
|
||||
foreach($operations as $r) {
|
||||
$query = 'DELETE FROM ' . $asf->getTableName()
|
||||
. ' WHERE ' . $this->getForeign() . ' = ?'
|
||||
. ' AND ' . $this->getLocal() . ' = ?';
|
||||
|
||||
$this->getTable()->getConnection()->execute($query, array($r->getIncremented(),$record->getIncremented()));
|
||||
}
|
||||
|
||||
$operations = Doctrine_Relation::getInsertOperations($record->obtainOriginals($alias),$new);
|
||||
|
||||
foreach($operations as $r) {
|
||||
$reldao = $asf->create();
|
||||
$reldao->set($this->getForeign(), $r);
|
||||
$reldao->set($this->getLocal(), $record);
|
||||
$reldao->save();
|
||||
}
|
||||
|
||||
$record->assignOriginals($alias, clone $record->get($alias));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* getRelationDql
|
||||
*
|
||||
|
@ -28,6 +28,35 @@ Doctrine::autoload('Doctrine_Relation');
|
||||
* @package Doctrine
|
||||
*/
|
||||
class Doctrine_Relation_ForeignKey extends Doctrine_Relation {
|
||||
/**
|
||||
* processDiff
|
||||
*
|
||||
* @param Doctrine_Record $record
|
||||
*/
|
||||
public function processDiff(Doctrine_Record $record) {
|
||||
$alias = $this->getAlias();
|
||||
|
||||
if($this->isOneToOne()) {
|
||||
if($record->obtainOriginals($alias) &&
|
||||
$record->obtainOriginals($alias)->obtainIdentifier() != $this->obtainReference($alias)->obtainIdentifier())
|
||||
$record->obtainOriginals($alias)->delete();
|
||||
} else {
|
||||
if($record->hasReference($alias)) {
|
||||
$new = $record->obtainReference($alias);
|
||||
|
||||
if( ! $record->obtainOriginals($alias))
|
||||
$record->loadReference($alias);
|
||||
|
||||
$operations = Doctrine_Relation::getDeleteOperations($record->obtainOriginals($alias), $new);
|
||||
|
||||
foreach($operations as $r) {
|
||||
$r->delete();
|
||||
}
|
||||
|
||||
$record->assignOriginals($alias, clone $record->get($alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* fetchRelatedFor
|
||||
*
|
||||
|
@ -28,6 +28,18 @@ Doctrine::autoload('Doctrine_Relation');
|
||||
* @package Doctrine
|
||||
*/
|
||||
class Doctrine_Relation_LocalKey extends Doctrine_Relation {
|
||||
/**
|
||||
* processDiff
|
||||
*
|
||||
* @param Doctrine_Record $record
|
||||
*/
|
||||
public function processDiff(Doctrine_Record $record) {
|
||||
$alias = $this->getAlias();
|
||||
|
||||
if($record->obtainOriginals($alias) &&
|
||||
$record->obtainOriginals($alias)->obtainIdentifier() != $this->references[$alias]->obtainIdentifier())
|
||||
$record->obtainOriginals($alias)->delete();
|
||||
}
|
||||
/**
|
||||
* fetchRelatedFor
|
||||
*
|
||||
|
@ -64,16 +64,14 @@ print '<pre>';
|
||||
|
||||
$test = new GroupTest('Doctrine Framework Unit Tests');
|
||||
|
||||
$test->addTestCase(new Doctrine_DataDict_Pgsql_TestCase());
|
||||
$test->addTestCase(new Doctrine_Record_TestCase());
|
||||
|
||||
$test->addTestCase(new Doctrine_DataDict_Pgsql_TestCase());
|
||||
|
||||
$test->addTestCase(new Doctrine_Relation_ManyToMany_TestCase());
|
||||
|
||||
|
||||
$test->addTestCase(new Doctrine_Relation_TestCase());
|
||||
|
||||
$test->addTestCase(new Doctrine_Record_TestCase());
|
||||
|
||||
$test->addTestCase(new Doctrine_Record_State_TestCase());
|
||||
|
||||
$test->addTestCase(new Doctrine_Import_TestCase());
|
||||
|
Loading…
x
Reference in New Issue
Block a user