1
0
mirror of synced 2024-12-14 07:06:04 +03:00

Removed some remainders from the old validation behaviour. This fixes a bug where records dont get inserted.

This commit is contained in:
romanb 2006-10-13 17:29:22 +00:00
parent 765c06b7c1
commit 2e8f73dba1

View File

@ -1,420 +1,401 @@
<?php <?php
/* /*
* $Id$ * $Id$
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>. * <http://www.phpdoctrine.com>.
*/ */
/** /**
* Doctrine_Connection_Transaction * Doctrine_Connection_Transaction
* *
* @package Doctrine ORM * @package Doctrine ORM
* @url www.phpdoctrine.com * @url www.phpdoctrine.com
* @license LGPL * @license LGPL
*/ */
class Doctrine_Connection_Transaction implements Countable, IteratorAggregate { class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
/** /**
* Doctrine_Connection_Transaction is in open state when it is opened and there are no active transactions * Doctrine_Connection_Transaction is in open state when it is opened and there are no active transactions
*/ */
const STATE_OPEN = 0; const STATE_OPEN = 0;
/** /**
* Doctrine_Connection_Transaction is in closed state when it is closed * Doctrine_Connection_Transaction is in closed state when it is closed
*/ */
const STATE_CLOSED = 1; const STATE_CLOSED = 1;
/** /**
* Doctrine_Connection_Transaction is in active state when it has one active transaction * Doctrine_Connection_Transaction is in active state when it has one active transaction
*/ */
const STATE_ACTIVE = 2; const STATE_ACTIVE = 2;
/** /**
* Doctrine_Connection_Transaction is in busy state when it has multiple active transactions * Doctrine_Connection_Transaction is in busy state when it has multiple active transactions
*/ */
const STATE_BUSY = 3; const STATE_BUSY = 3;
/** /**
* @var Doctrine_Connection $conn the connection object * @var Doctrine_Connection $conn the connection object
*/ */
private $conn; private $conn;
/** /**
* @see Doctrine_Connection_Transaction::STATE_* constants * @see Doctrine_Connection_Transaction::STATE_* constants
* @var boolean $state the current state of the connection * @var boolean $state the current state of the connection
*/ */
private $state = 0; private $state = 0;
/** /**
* @var integer $transaction_level the nesting level of transactions, used by transaction methods * @var integer $transaction_level the nesting level of transactions, used by transaction methods
*/ */
private $transaction_level = 0; private $transaction_level = 0;
/** /**
* @var Doctrine_Validator $validator transaction validator * @var array $invalid an array containing all invalid records within this transaction
*/ */
private $validator; protected $invalid = array();
/** /**
* @var array $invalid an array containing all invalid records within this transaction * @var array $update two dimensional pending update list, the records in
*/ * this list will be updated when transaction is committed
protected $invalid = array(); */
/** protected $update = array();
* @var array $update two dimensional pending update list, the records in /**
* this list will be updated when transaction is committed * @var array $insert two dimensional pending insert list, the records in
*/ * this list will be inserted when transaction is committed
protected $update = array(); */
/** protected $insert = array();
* @var array $insert two dimensional pending insert list, the records in /**
* this list will be inserted when transaction is committed * @var array $delete two dimensional pending delete list, the records in
*/ * this list will be deleted when transaction is committed
protected $insert = array(); */
/** protected $delete = array();
* @var array $delete two dimensional pending delete list, the records in /**
* this list will be deleted when transaction is committed * the constructor
*/ *
protected $delete = array(); * @param Doctrine_Connection $conn
/** */
* the constructor public function __construct(Doctrine_Connection $conn) {
* $this->conn = $conn;
* @param Doctrine_Connection $conn $this->state = Doctrine_Connection_Transaction::STATE_OPEN;
*/ }
public function __construct(Doctrine_Connection $conn) { /**
$this->conn = $conn; * returns the state of this connection
$this->state = Doctrine_Connection_Transaction::STATE_OPEN; *
} * @see Doctrine_Connection_Transaction::STATE_* constants
/** * @return integer the connection state
* returns the state of this connection */
* public function getState() {
* @see Doctrine_Connection_Transaction::STATE_* constants return $this->state;
* @return integer the connection state }
*/ /**
public function getState() { * get the current transaction nesting level
return $this->state; *
} * @return integer
/** */
* get the current transaction nesting level public function getTransactionLevel() {
* return $this->transaction_level;
* @return integer }
*/ /**
public function getTransactionLevel() { * beginTransaction
return $this->transaction_level; * starts a new transaction
} * if the lockmode is long this starts db level transaction
/** *
* beginTransaction * @return void
* starts a new transaction */
* if the lockmode is long this starts db level transaction public function beginTransaction() {
* if($this->transaction_level == 0) {
* @return void
*/ if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) {
public function beginTransaction() { $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);
if($this->transaction_level == 0) {
if($this->conn->getAttribute(Doctrine::ATTR_VLD)) $this->conn->getDBH()->beginTransaction();
$this->validator = new Doctrine_Validator();
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn);
if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) { }
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn); $this->state = Doctrine_Connection_Transaction::STATE_ACTIVE;
} else {
$this->conn->getDBH()->beginTransaction(); $this->state = Doctrine_Connection_Transaction::STATE_BUSY;
}
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn); $this->transaction_level++;
} }
$this->state = Doctrine_Connection_Transaction::STATE_ACTIVE; /**
} else { * commits the current transaction
$this->state = Doctrine_Connection_Transaction::STATE_BUSY; * if lockmode is short this method starts a transaction
} * and commits it instantly
$this->transaction_level++; *
} * @throws Doctrine_Connection_Transaction_Exception if the transaction fails at PDO level
/** * @throws Doctrine_Validator_Exception if the transaction fails due to record validations
* commits the current transaction * @return void
* if lockmode is short this method starts a transaction */
* and commits it instantly public function commit() {
*
* @throws Doctrine_Connection_Transaction_Exception if the transaction fails at PDO level $this->transaction_level--;
* @throws Doctrine_Validator_Exception if the transaction fails due to record validations
* @return void if($this->transaction_level == 0) {
*/ $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn);
public function commit() {
if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) {
$this->transaction_level--; $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);
if($this->transaction_level == 0) { $this->conn->getDBH()->beginTransaction();
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn);
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn);
if($this->conn->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) { }
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);
try {
$this->conn->getDBH()->beginTransaction(); $this->bulkDelete();
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn); } catch(Exception $e) {
} $this->rollback();
try { throw new Doctrine_Exception($e->__toString());
$this->bulkDelete(); }
} catch(Exception $e) { if(count($this->invalid) > 0) {
$this->rollback(); $this->rollback();
throw new Doctrine_Exception($e->__toString()); $tmp = $this->invalid;
} $this->invalid = array();
if(count($this->invalid) > 0) { throw new Doctrine_Validator_Exception($tmp);
$this->rollback(); }
$tmp = $this->invalid; $this->conn->getDBH()->commit();
$this->invalid = array();
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this->conn);
throw new Doctrine_Validator_Exception($tmp);
} $this->state = Doctrine_Connection_Transaction::STATE_OPEN;
$this->invalid = array();
$this->conn->getDBH()->commit();
} elseif($this->transaction_level == 1)
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this->conn); $this->state = Doctrine_Connection_Transaction::STATE_ACTIVE;
}
$this->state = Doctrine_Connection_Transaction::STATE_OPEN; /**
$this->validator = null; * rollback
$this->invalid = array(); * rolls back all transactions
*
} elseif($this->transaction_level == 1) * this method also listens to onPreTransactionRollback and onTransactionRollback
$this->state = Doctrine_Connection_Transaction::STATE_ACTIVE; * eventlisteners
} *
/** * @return void
* rollback */
* rolls back all transactions public function rollback() {
* $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this->conn);
* this method also listens to onPreTransactionRollback and onTransactionRollback
* eventlisteners $this->delete = array();
* $this->insert = array();
* @return void $this->update = array();
*/
public function rollback() { $this->transaction_level = 0;
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this->conn); $this->conn->getDBH()->rollback();
$this->state = Doctrine_Connection_Transaction::STATE_OPEN;
$this->delete = array();
$this->insert = array(); $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this->conn);
$this->update = array(); }
/**
$this->transaction_level = 0; * bulkDelete
$this->conn->getDBH()->rollback(); * deletes all records from the pending delete list
$this->state = Doctrine_Connection_Transaction::STATE_OPEN; *
* @return void
$this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this->conn); */
} public function bulkDelete() {
/** foreach($this->delete as $name => $deletes) {
* bulkDelete $record = false;
* deletes all records from the pending delete list $ids = array();
* foreach($deletes as $k => $record) {
* @return void $ids[] = $record->getIncremented();
*/ $record->assignIdentifier(false);
public function bulkDelete() { }
foreach($this->delete as $name => $deletes) { if($record instanceof Doctrine_Record) {
$record = false; $params = substr(str_repeat("?, ",count($ids)),0,-2);
$ids = array(); $query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$record->getTable()->getIdentifier()." IN(".$params.")";
foreach($deletes as $k => $record) { $this->conn->execute($query,$ids);
$ids[] = $record->getIncremented(); }
$record->assignIdentifier(false);
} }
if($record instanceof Doctrine_Record) { $this->delete = array();
$params = substr(str_repeat("?, ",count($ids)),0,-2); }
$query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$record->getTable()->getIdentifier()." IN(".$params.")"; /**
$this->conn->execute($query,$ids); * updates the given record
} *
* @param Doctrine_Record $record
} * @return boolean
$this->delete = array(); */
} public function update(Doctrine_Record $record) {
/** $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
* updates the given record
* $array = $record->getPrepared();
* @param Doctrine_Record $record
* @return boolean if(empty($array))
*/ return false;
public function update(Doctrine_Record $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record); $set = array();
foreach($array as $name => $value):
$array = $record->getPrepared(); $set[] = $name." = ?";
if(empty($array)) if($value instanceof Doctrine_Record) {
return false; switch($value->getState()):
case Doctrine_Record::STATE_TCLEAN:
$set = array(); case Doctrine_Record::STATE_TDIRTY:
foreach($array as $name => $value): $record->save();
$set[] = $name." = ?"; default:
$array[$name] = $value->getIncremented();
if($value instanceof Doctrine_Record) { $record->set($name, $value->getIncremented());
switch($value->getState()): endswitch;
case Doctrine_Record::STATE_TCLEAN: }
case Doctrine_Record::STATE_TDIRTY: endforeach;
$record->save();
default: $params = array_values($array);
$array[$name] = $value->getIncremented(); $id = $record->obtainIdentifier();
$record->set($name, $value->getIncremented());
endswitch;
} if( ! is_array($id))
endforeach; $id = array($id);
if(isset($this->validator)) { $id = array_values($id);
if( ! $this->validator->validateRecord($record)) { $params = array_merge($params, $id);
return false;
}
} $sql = "UPDATE ".$record->getTable()->getTableName()." SET ".implode(", ",$set)." WHERE ".implode(" = ? AND ",$record->getTable()->getPrimaryKeys())." = ?";
$params = array_values($array); $stmt = $this->conn->getDBH()->prepare($sql);
$id = $record->obtainIdentifier(); $stmt->execute($params);
$record->assignIdentifier(true);
if( ! is_array($id))
$id = array($id); $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
$id = array_values($id); return true;
$params = array_merge($params, $id); }
/**
* inserts a record into database
$sql = "UPDATE ".$record->getTable()->getTableName()." SET ".implode(", ",$set)." WHERE ".implode(" = ? AND ",$record->getTable()->getPrimaryKeys())." = ?"; *
* @param Doctrine_Record $record
$stmt = $this->conn->getDBH()->prepare($sql); * @return boolean
$stmt->execute($params); */
public function insert(Doctrine_Record $record) {
$record->assignIdentifier(true); // listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
$array = $record->getPrepared();
return true;
} if(empty($array))
/** return false;
* inserts a record into database
* $table = $record->getTable();
* @param Doctrine_Record $record $keys = $table->getPrimaryKeys();
* @return boolean
*/
public function insert(Doctrine_Record $record) {
// listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record); $seq = $record->getTable()->getSequenceName();
$array = $record->getPrepared(); if( ! empty($seq)) {
$id = $this->getNextID($seq);
if(empty($array)) $name = $record->getTable()->getIdentifier();
return false; $array[$name] = $id;
}
$table = $record->getTable();
$keys = $table->getPrimaryKeys(); $strfields = join(", ", array_keys($array));
$strvalues = substr(str_repeat("?, ",count($array)),0,-2);
$sql = "INSERT INTO ".$record->getTable()->getTableName()." (".$strfields.") VALUES (".$strvalues.")";
$stmt = $this->conn->getDBH()->prepare($sql);
$seq = $record->getTable()->getSequenceName();
$stmt->execute(array_values($array));
if( ! empty($seq)) {
$id = $this->getNextID($seq);
$name = $record->getTable()->getIdentifier(); if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
$array[$name] = $id; $id = $this->conn->getDBH()->lastInsertID();
}
if( ! $id)
if(isset($this->validator)) { $id = $table->getMaxIdentifier();
if( ! $this->validator->validateRecord($record)) {
return false; $record->assignIdentifier($id);
} } else
} $record->assignIdentifier(true);
$strfields = join(", ", array_keys($array)); // listen the onInsert event
$strvalues = substr(str_repeat("?, ",count($array)),0,-2); $table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
$sql = "INSERT INTO ".$record->getTable()->getTableName()." (".$strfields.") VALUES (".$strvalues.")";
return true;
$stmt = $this->conn->getDBH()->prepare($sql); }
/**
$stmt->execute(array_values($array)); * adds record into pending insert list
* @param Doctrine_Record $record
*/
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) { public function addInsert(Doctrine_Record $record) {
$id = $this->conn->getDBH()->lastInsertID(); $name = $record->getTable()->getComponentName();
$this->insert[$name][] = $record;
if( ! $id) }
$id = $table->getMaxIdentifier(); /**
* adds record into penging update list
$record->assignIdentifier($id); * @param Doctrine_Record $record
} else */
$record->assignIdentifier(true); public function addUpdate(Doctrine_Record $record) {
$name = $record->getTable()->getComponentName();
// listen the onInsert event $this->update[$name][] = $record;
$table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record); }
/**
return true; * adds record into pending delete list
} * @param Doctrine_Record $record
/** */
* adds record into pending insert list public function addDelete(Doctrine_Record $record) {
* @param Doctrine_Record $record $name = $record->getTable()->getComponentName();
*/ $this->delete[$name][] = $record;
public function addInsert(Doctrine_Record $record) { }
$name = $record->getTable()->getComponentName(); /**
$this->insert[$name][] = $record; * addInvalid
} * adds record into invalid records list
/** *
* adds record into penging update list * @param Doctrine_Record $record
* @param Doctrine_Record $record * @return boolean false if record already existed in invalid records list,
*/ * otherwise true
public function addUpdate(Doctrine_Record $record) { */
$name = $record->getTable()->getComponentName(); public function addInvalid(Doctrine_Record $record) {
$this->update[$name][] = $record; if(in_array($record, $this->invalid))
} return false;
/**
* adds record into pending delete list $this->invalid[] = $record;
* @param Doctrine_Record $record return true;
*/ }
public function addDelete(Doctrine_Record $record) { /**
$name = $record->getTable()->getComponentName(); * returns the pending insert list
$this->delete[$name][] = $record; *
} * @return array
/** */
* addInvalid public function getInserts() {
* adds record into invalid records list return $this->insert;
* }
* @param Doctrine_Record $record /**
* @return boolean false if record already existed in invalid records list, * returns the pending update list
* otherwise true *
*/ * @return array
public function addInvalid(Doctrine_Record $record) { */
if(in_array($record, $this->invalid)) public function getUpdates() {
return false; return $this->update;
}
$this->invalid[] = $record; /**
return true; * returns the pending delete list
} *
/** * @return array
* returns the pending insert list */
* public function getDeletes() {
* @return array return $this->delete;
*/ }
public function getInserts() { public function getIterator() { }
return $this->insert; /**
} * an alias for getTransactionLevel
/** *
* returns the pending update list * @return integer returns the nesting level of this transaction
* */
* @return array public function count() {
*/ return $this->transaction_level;
public function getUpdates() { }
return $this->update; }
}
/**
* returns the pending delete list
*
* @return array
*/
public function getDeletes() {
return $this->delete;
}
public function getIterator() { }
/**
* an alias for getTransactionLevel
*
* @return integer returns the nesting level of this transaction
*/
public function count() {
return $this->transaction_level;
}
}