From 0d75147c7057a6ca777799e625afa89917377e8a Mon Sep 17 00:00:00 2001 From: zYne Date: Thu, 26 Oct 2006 11:24:35 +0000 Subject: [PATCH] Doctrine_Expression classes added, fixes #195, #196, #197, #198 Ticket: 195 --- lib/Doctrine/DataDict.php | 3 +- lib/Doctrine/DataDict/Pgsql.php | 12 +- lib/Doctrine/Expression.php | 233 +++++++ lib/Doctrine/Expression/Oracle.php | 45 ++ lib/Doctrine/Expression/Pgsql.php | 91 +++ lib/Doctrine/Expression/Sqlite.php | 49 ++ lib/Doctrine/Filter.php | 14 - lib/Doctrine/Session.php | 943 ----------------------------- 8 files changed, 426 insertions(+), 964 deletions(-) create mode 100644 lib/Doctrine/Expression.php create mode 100644 lib/Doctrine/Expression/Oracle.php create mode 100644 lib/Doctrine/Expression/Pgsql.php create mode 100644 lib/Doctrine/Expression/Sqlite.php delete mode 100644 lib/Doctrine/Filter.php delete mode 100644 lib/Doctrine/Session.php diff --git a/lib/Doctrine/DataDict.php b/lib/Doctrine/DataDict.php index 7b954ce8a..b1dd8f228 100644 --- a/lib/Doctrine/DataDict.php +++ b/lib/Doctrine/DataDict.php @@ -30,7 +30,7 @@ class Doctrine_DataDict { protected $dbh; - public function __construct(PDO $dbh) { + public function __construct($dbh = null) { $file = Doctrine::getPath().DIRECTORY_SEPARATOR."Doctrine".DIRECTORY_SEPARATOR."adodb-hack".DIRECTORY_SEPARATOR."adodb.inc.php"; if( ! file_exists($file)) @@ -39,6 +39,7 @@ class Doctrine_DataDict { require_once($file); $this->dbh = $dbh; + if($dbh) $this->dict = NewDataDictionary($dbh); } /** diff --git a/lib/Doctrine/DataDict/Pgsql.php b/lib/Doctrine/DataDict/Pgsql.php index e0569ba4c..f61079ccf 100644 --- a/lib/Doctrine/DataDict/Pgsql.php +++ b/lib/Doctrine/DataDict/Pgsql.php @@ -29,7 +29,7 @@ * @version $Id$ */ -class Doctrine_DataDict_Mysql extends Doctrine_DataDict { +class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. @@ -53,7 +53,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict { * @return string DBMS specific SQL code portion that should be used to * declare the specified field. */ - public function getTypeDeclaration(array $field) { + public function getNativeDeclaration(array $field) { switch ($field['type']) { case 'string': case 'array': @@ -115,8 +115,8 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict { * @author Lukas Smith (PEAR MDB2 library) * @return array containing the various possible types, length, sign, fixed */ - public function mapNativeDatatype($field) { - $db_type = preg_replace('/\d/','', strtolower($field['type']) ); + public function getDoctrineDeclaration(array $field) { + $length = $field['length']; if ($length == '-1' && !empty($field['atttypmod'])) { $length = $field['atttypmod'] - 4; @@ -126,7 +126,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict { } $type = array(); $unsigned = $fixed = null; - switch ($db_type) { + switch ($field['type']) { case 'smallint': case 'int2': $type[] = 'integer'; @@ -159,7 +159,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict { case 'bool': case 'boolean': $type[] = 'boolean'; - $length = null; + $length = 1; break; case 'text': case 'varchar': diff --git a/lib/Doctrine/Expression.php b/lib/Doctrine/Expression.php new file mode 100644 index 000000000..0201309f8 --- /dev/null +++ b/lib/Doctrine/Expression.php @@ -0,0 +1,233 @@ +. + */ + +/** + * Doctrine_Expression + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_Expression { + + /** + * @var Doctrine_Connection $connection + */ + protected $conn; + /** + * @param Doctrine_Connection $conn + */ + public function __construct(Doctrine_Connection $conn) { + $this->conn = $conn; + } + /** + * Returns the average value of a column + * + * @param string $column the column to use + * @return string generated sql including an AVG aggregate function + */ + public function avg($column) { + $column = $this->getIdentifier($column); + return 'AVG(' . $column . ')'; + } + + /** + * Returns the number of rows (without a NULL value) of a column + * + * If a '*' is used instead of a column the number of selected rows + * is returned. + * + * @param string|integer $column the column to use + * @return string generated sql including a COUNT aggregate function + */ + public function count($column) { + $column = $this->getIdentifier($column); + return 'COUNT(' . $column . ')'; + } + + /** + * Returns the highest value of a column + * + * @param string $column the column to use + * @return string generated sql including a MAX aggregate function + */ + public function max($column) { + $column = $this->getIdentifier($column); + return 'MAX(' . $column . ')'; + } + + /** + * Returns the lowest value of a column + * + * @param string $column the column to use + * @return string + */ + public function min($column) { + $column = $this->getIdentifier($column); + return 'MIN(' . $column . ')'; + } + + /** + * Returns the total sum of a column + * + * @param string $column the column to use + * @return string + */ + public function sum($column) { + $column = $this->getIdentifier($column); + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the md5 sum of a field. + * + * Note: Not SQL92, but common functionality + * + * @return string + */ + public function md5($column) { + $column = $this->getIdentifier($column); + return 'MD5(' . $column . ')'; + } + + /** + * Returns the length of a text field. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function length($column) { + $column = $this->getIdentifier($column); + return 'LENGTH(' . $column . ')'; + } + + /** + * Rounds a numeric field to the number of decimals specified. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function round($column, $decimals) { + $column = $this->getIdentifier($column); + + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the remainder of the division operation + * $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function mod($expression1, $expression2) { + $expression1 = $this->getIdentifier($expression1); + $expression2 = $this->getIdentifier($expression2); + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + /** + * ltrim + * returns the string $str with leading space characters removed + * + * @param string $str literal string or column name + * @return string + */ + public function ltrim($str) { + return 'LTRIM(' . $str . ')'; + } + /** + * upper + * Returns the string $str with all characters changed to uppercase according to the current character set mapping. + * + * @param string $str literal string or column name + * @return string + */ + public function upper($str) { + return 'UPPER(' . $str . ')'; + } + /** + * lower + * Returns the string $str with all characters changed to lowercase according to the current character set mapping. + * + * @param string $str literal string or column name + * @return string + */ + public function lower($str) { + return 'LOWER(' . $str . ')'; + } + /** + * locate + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string + * @param string $str literal string + * @return string + */ + public function locate($substr, $str) { + return 'LOCATE(' . $str . ', ' . $substr . ')'; + } + /** + * Returns the current system date. + * + * @return string + */ + public function now() { + return 'NOW()'; + } + /** + * Returns part of a string. + * + * Note: Not SQL92, but common functionality. + * + * @param string $value the target $value the string or the string column. + * @param int $from extract from this characeter. + * @param int $len extract this amount of characters. + * @return string sql that extracts part of a string. + */ + public function subString($value, $from, $len = null) { + $value = $this->getIdentifier($value); + if ($len === null) + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + else { + $len = $this->getIdentifier($len); + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $len . ')'; + } + } + + /** + * Returns a series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression or an array with expressions. + * + * @param string|array(string) strings that will be concatinated. + */ + public function concat($arg1, $arg2) { + $args = func_get_args(); + $cols = $this->getIdentifiers($cols); + return 'CONCAT(' . join(', ', $cols) . ')'; + } +} diff --git a/lib/Doctrine/Expression/Oracle.php b/lib/Doctrine/Expression/Oracle.php new file mode 100644 index 000000000..9de43f2b2 --- /dev/null +++ b/lib/Doctrine/Expression/Oracle.php @@ -0,0 +1,45 @@ +. + */ +Doctrine::autoload('Doctrine_Expression'); +/** + * Doctrine_Expression_Sqlite + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_Expression_Oracle extends Doctrine_Expression { + /** + * Returns a series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression + * + * @param string $arg1, $arg2 ... $argN strings that will be concatinated. + * @return string + */ + public function concat($arg1, $arg2) { + $args = func_get_args(); + + $cols = $this->getIdentifiers( $args ); + return join( ' || ' , $cols ); + } +} diff --git a/lib/Doctrine/Expression/Pgsql.php b/lib/Doctrine/Expression/Pgsql.php new file mode 100644 index 000000000..8e13e6326 --- /dev/null +++ b/lib/Doctrine/Expression/Pgsql.php @@ -0,0 +1,91 @@ +/* + * $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 + * . + */ +Doctrine::autoload('Doctrine_Expression'); +/** + * Doctrine_Expression_Pgsql + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_Expression_Pgsql extends Doctrine_Expression { + /** + * Returns the md5 sum of a field. + * + * Note: Not SQL92, but common functionality + * + * md5() works with the default PostgreSQL 8 versions. + * + * If you are using PostgreSQL 7.x or older you need + * to make sure that the digest procedure. + * If you use RPMS (Redhat and Mandrake) install the postgresql-contrib + * package. You must then install the procedure by running this shell command: + * + * psql [dbname] < /usr/share/pgsql/contrib/pgcrypto.sql + * + * You should make sure you run this as the postgres user. + * + * @return string + */ + public function md5($column) { + $column = $this->getIdentifier($column); + + if ($this->version > 7) + return 'MD5(' . $column . ')'; + else + return 'encode(digest(' . $column .', md5), hex)'; + } + + /** + * Returns part of a string. + * + * Note: Not SQL92, but common functionality. + * + * @param string $value the target $value the string or the string column. + * @param int $from extract from this characeter. + * @param int $len extract this amount of characters. + * @return string sql that extracts part of a string. + */ + public function subString($value, $from, $len = null) { + $value = $this->getIdentifier($value); + + if ($len === null) { + $len = $this->getIdentifier($len); + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } else + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; + } + + /** + * Returns a series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression or an array with expressions. + * + * @param string|array(string) strings that will be concatinated. + * @return string + */ + public function concat($arg1, $arg2) { + $args = func_get_args(); + $cols = $this->getIdentifiers($cols); + + return join(' || ' , $cols); + } +} diff --git a/lib/Doctrine/Expression/Sqlite.php b/lib/Doctrine/Expression/Sqlite.php new file mode 100644 index 000000000..76dbe987b --- /dev/null +++ b/lib/Doctrine/Expression/Sqlite.php @@ -0,0 +1,49 @@ +. + */ +Doctrine::autoload('Doctrine_Expression'); +/** + * Doctrine_Expression_Sqlite + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_Expression_Sqlite extends Doctrine_Expression { + /** + * Returns part of a string. + * + * Note: Not SQL92, but common functionality. SQLite only supports the 3 + * parameter variant of this function, so we are using 2^30-1 as + * artificial length in that case. + * + * @param string $value the target $value the string or the string column. + * @param int $from extract from this characeter. + * @param int $len extract this amount of characters. + * @return string sql that extracts part of a string. + */ + public function subString($value, $from, $len = null) { + $value = $this->getIdentifier( $value ); + if ( $len === null ) + $len = 1073741823; + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; + } +} diff --git a/lib/Doctrine/Filter.php b/lib/Doctrine/Filter.php deleted file mode 100644 index 64385851f..000000000 --- a/lib/Doctrine/Filter.php +++ /dev/null @@ -1,14 +0,0 @@ -name = $name; - } - - public function getName() { - return $this->name; - } -} - diff --git a/lib/Doctrine/Session.php b/lib/Doctrine/Session.php deleted file mode 100644 index 4c9ef5ab3..000000000 --- a/lib/Doctrine/Session.php +++ /dev/null @@ -1,943 +0,0 @@ -. - */ - -/** - * Doctrine_Session - * - * @package Doctrine ORM - * @url www.phpdoctrine.com - * @license LGPL - */ -abstract class Doctrine_Session extends Doctrine_Configurable implements Countable, IteratorAggregate { - /** - * Doctrine_Session is in open state when it is opened and there are no active transactions - */ - const STATE_OPEN = 0; - /** - * Doctrine_Session is in closed state when it is closed - */ - const STATE_CLOSED = 1; - /** - * Doctrine_Session is in active state when it has one active transaction - */ - const STATE_ACTIVE = 2; - /** - * Doctrine_Session is in busy state when it has multiple active transactions - */ - const STATE_BUSY = 3; - /** - * @var $dbh the database handle - */ - private $dbh; - /** - * @see Doctrine_Session::STATE_* constants - * @var boolean $state the current state of the session - */ - private $state = 0; - /** - * @var integer $transaction_level the nesting level of transactions, used by transaction methods - */ - private $transaction_level = 0; - /** - * @var array $tables an array containing all the initialized Doctrine_Table objects - * keys representing Doctrine_Table component names and values as Doctrine_Table objects - */ - protected $tables = array(); - /** - * @var Doctrine_Validator $validator transaction validator - */ - protected $validator; - /** - * @var array $update two dimensional pending update list, the records in - * this list will be updated when transaction is committed - */ - protected $update = array(); - /** - * @var array $insert two dimensional pending insert list, the records in - * this list will be inserted when transaction is committed - */ - protected $insert = array(); - /** - * @var array $delete two dimensional pending delete list, the records in - * this list will be deleted when transaction is committed - */ - protected $delete = array(); - - - - - /** - * the constructor - * - * @param Doctrine_Manager $manager the manager object - * @param PDO $pdo the database handler - */ - public function __construct(Doctrine_Manager $manager,PDO $pdo) { - $this->dbh = $pdo; - $this->state = Doctrine_Session::STATE_OPEN; - - $this->setParent($manager); - - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); - $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - - $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this); - } - /** - * returns the state of this session - * - * @see Doctrine_Session::STATE_* constants - * @return integer the session state - */ - public function getState() { - return $this->state; - } - /** - * returns the manager that created this session - * - * @return Doctrine_Manager - */ - public function getManager() { - return $this->getParent(); - } - /** - * returns the database handler of which this session uses - * - * @return object PDO the database handler - */ - public function getDBH() { - return $this->dbh; - } - /** - * query - * queries the database with Doctrine Query Language - * - * @param string $query DQL query - * @param array $params query parameters - */ - final public function query($query,array $params = array()) { - $parser = new Doctrine_Query($this); - - return $parser->query($query, $params); - } - /** - * queries the database with limit and offset - * added to the query and returns a PDOStatement object - * - * @param string $query - * @param integer $limit - * @param integer $offset - * @return PDOStatement - */ - public function select($query,$limit = 0,$offset = 0) { - if($limit > 0 || $offset > 0) - $query = $this->modifyLimitQuery($query,$limit,$offset); - - return $this->dbh->query($query); - } - /** - * @param string $query sql query - * @param array $params query parameters - * - * @return PDOStatement - */ - public function execute($query, array $params = array()) { - if( ! empty($params)) { - $stmt = $this->dbh->prepare($query); - $stmt->execute($params); - return $stmt; - } else { - return $this->dbh->query($query); - } - } - /** - * whether or not this session has table $name initialized - * - * @param $mixed $name - * @return boolean - */ - public function hasTable($name) { - return isset($this->tables[$name]); - } - /** - * returns a table object for given component name - * - * @param string $name component name - * @return object Doctrine_Table - */ - public function getTable($name) { - if(isset($this->tables[$name])) - return $this->tables[$name]; - - $class = $name."Table"; - - if(class_exists($class) && in_array("Doctrine_Table", class_parents($class))) { - return new $class($name); - } else { - - return new Doctrine_Table($name); - } - } - /** - * returns an array of all initialized tables - * - * @return array - */ - public function getTables() { - return $this->tables; - } - /** - * returns an iterator that iterators through all - * initialized table objects - * - * @return ArrayIterator - */ - public function getIterator() { - return new ArrayIterator($this->tables); - } - /** - * returns the count of initialized table objects - * - * @return integer - */ - public function count() { - return count($this->tables); - } - /** - * @param $objTable a Doctrine_Table object to be added into registry - * @return boolean - */ - public function addTable(Doctrine_Table $objTable) { - $name = $objTable->getComponentName(); - - if(isset($this->tables[$name])) - return false; - - $this->tables[$name] = $objTable; - return true; - } - /** - * creates a record - * - * create creates a record - * @param string $name component name - * @return Doctrine_Record Doctrine_Record object - */ - public function create($name) { - return $this->getTable($name)->create(); - } - - - /** - * buildFlushTree - * builds a flush tree that is used in transactions - * - * @return array - */ - public function buildFlushTree(array $tables) { - $tree = array(); - foreach($tables as $k => $table) { - $k = $k.$table; - if( ! ($table instanceof Doctrine_Table)) - $table = $this->getTable($table); - - $nm = $table->getComponentName(); - - $index = array_search($nm,$tree); - if($index === false) { - $tree[] = $nm; - $index = max(array_keys($tree)); - - //print "$k -- adding $nm...
"; - } - - $rels = $table->getRelations(); - - // group relations - - foreach($rels as $key => $rel) { - if($rel instanceof Doctrine_Relation_ForeignKey) { - 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; - - if($rel instanceof Doctrine_Relation_ForeignKey) { - if($index2 !== false) { - if($index2 >= $index) - continue; - - unset($tree[$index]); - array_splice($tree,$index2,0,$nm); - $index = $index2; - - //print "$k -- pushing $nm into $index2...
"; - - } else { - $tree[] = $name; - //print "$k -- adding $nm :$name...
"; - } - - } elseif($rel instanceof Doctrine_Relation_LocalKey) { - if($index2 !== false) { - if($index2 <= $index) - continue; - - unset($tree[$index2]); - array_splice($tree,$index,0,$name); - - //print "$k -- pushing $name into $index...
"; - - } else { - //array_splice($tree, $index, 0, $name); - array_unshift($tree,$name); - $index++; - - //print "$k -- pushing $name into 0...
"; - } - } elseif($rel instanceof Doctrine_Relation_Association) { - $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; - - //print "$k -- pushing $nm into $index3...
"; - - } else { - $tree[] = $n; - //print "$k -- adding $nm :$name...
"; - } - } - //print_r($tree); - } - //print_r($tree); - - } - return array_values($tree); - } - - /** - * flush - * saves all the records from all tables - * this operation is isolated using a transaction - * - * @return void - */ - public function flush() { - $this->beginTransaction(); - $this->saveAll(); - $this->commit(); - } - /** - * saveAll - * saves all the records from all tables - * - * @return void - */ - private function saveAll() { - $tree = $this->buildFlushTree($this->tables); - - foreach($tree as $name) { - $table = $this->tables[$name]; - - foreach($table->getRepository() as $record) { - $this->save($record); - } - } - foreach($tree as $name) { - $table = $this->tables[$name]; - foreach($table->getRepository() as $record) { - $record->saveAssociations(); - } - } - } - /** - * clear - * clears all repositories - * - * @return void - */ - public function clear() { - foreach($this->tables as $k => $table) { - $table->getRepository()->evictAll(); - $table->clear(); - } - } - /** - * @return void - */ - public function evictTables() { - $this->tables = array(); - } - /** - * close - * closes the session - * - * @return void - */ - public function close() { - $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreClose($this); - - $this->clear(); - $this->state = Doctrine_Session::STATE_CLOSED; - - $this->getAttribute(Doctrine::ATTR_LISTENER)->onClose($this); - } - /** - * get the current transaction nesting level - * - * @return integer - */ - public function getTransactionLevel() { - return $this->transaction_level; - } - /** - * beginTransaction - * starts a new transaction - * @return void - */ - public function beginTransaction() { - if($this->transaction_level == 0) { - - if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) { - $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this); - $this->dbh->beginTransaction(); - $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this); - } - $this->state = Doctrine_Session::STATE_ACTIVE; - } else { - $this->state = Doctrine_Session::STATE_BUSY; - } - $this->transaction_level++; - } - /** - * commits the current transaction - * if lockmode is optimistic this method starts a transaction - * and commits it instantly - * - * @return void - */ - public function commit() { - - $this->transaction_level--; - - if($this->transaction_level == 0) { - - - if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) { - $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this); - - $this->dbh->beginTransaction(); - - $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this); - } - - if($this->getAttribute(Doctrine::ATTR_VLD)) - $this->validator = new Doctrine_Validator(); - - try { - - $this->bulkInsert(); - $this->bulkUpdate(); - $this->bulkDelete(); - - if($this->getAttribute(Doctrine::ATTR_VLD)) { - if($this->validator->hasErrors()) { - $this->rollback(); - throw new Doctrine_Validator_Exception($this->validator); - } - } - - $this->dbh->commit(); - - } catch(PDOException $e) { - $this->rollback(); - - throw new Doctrine_Exception($e->__toString()); - } - - $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this); - - $this->delete = array(); - $this->state = Doctrine_Session::STATE_OPEN; - - $this->validator = null; - - } elseif($this->transaction_level == 1) - $this->state = Doctrine_Session::STATE_ACTIVE; - } - /** - * rollback - * rolls back all transactions - * - * this method also listens to onPreTransactionRollback and onTransactionRollback - * eventlisteners - * - * @return void - */ - public function rollback() { - $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this); - - $this->transaction_level = 0; - $this->dbh->rollback(); - $this->state = Doctrine_Session::STATE_OPEN; - - $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this); - } - /** - * bulkInsert - * inserts all the objects in the pending insert list into database - * @return void - */ - public function bulkInsert() { - if(empty($this->insert)) - return false; - - foreach($this->insert as $name => $inserts) { - if( ! isset($inserts[0])) - continue; - - $record = $inserts[0]; - $table = $record->getTable(); - $seq = $table->getSequenceName(); - - $increment = false; - $keys = $table->getPrimaryKeys(); - $id = null; - - if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) { - $increment = true; - } - - foreach($inserts as $k => $record) { - $table->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record); - // listen the onPreInsert event - $table->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record); - - - $this->insert($record); - if($increment) { - if($k == 0) { - // record uses auto_increment column - - $id = $this->dbh->lastInsertID(); - - if( ! $id) - $id = $table->getMaxIdentifier(); - } - - $record->assignIdentifier($id); - $id++; - } else - $record->assignIdentifier(true); - - // listen the onInsert event - $table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record); - - $table->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record); - } - } - $this->insert = array(); - return true; - } - /** - * 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; - } - /** - * bulkUpdate - * updates all objects in the pending update list - * - * @return void - */ - public function bulkUpdate() { - foreach($this->update as $name => $updates) { - $ids = array(); - - foreach($updates as $k => $record) { - $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record); - // listen the onPreUpdate event - $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record); - - $this->update($record); - // listen the onUpdate event - $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record); - - $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record); - } - } - $this->update = array(); - } - /** - * bulkDelete - * deletes all records from the pending delete list - * - * @return void - */ - public function bulkDelete() { - foreach($this->delete as $name => $deletes) { - $record = false; - $ids = array(); - foreach($deletes as $k => $record) { - $ids[] = $record->getIncremented(); - $record->assignIdentifier(false); - } - if($record instanceof Doctrine_Record) { - $table = $record->getTable(); - - $params = substr(str_repeat("?, ",count($ids)),0,-2); - $query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$table->getIdentifier()." IN(".$params.")"; - $this->execute($query,$ids); - - $record->getTable()->getCache()->deleteMultiple($ids); - } - } - $this->delete = array(); - } - /** - * 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 - * - * @param Doctrine_Record $record - * @return void - */ - public function save(Doctrine_Record $record) { - switch($record->getState()): - case Doctrine_Record::STATE_TDIRTY: - $this->addInsert($record); - break; - case Doctrine_Record::STATE_DIRTY: - case Doctrine_Record::STATE_PROXY: - $this->addUpdate($record); - break; - case Doctrine_Record::STATE_CLEAN: - case Doctrine_Record::STATE_TCLEAN: - // do nothing - break; - endswitch; - } - /** - * saves all related records to $record - * - * @param Doctrine_Record $record - */ - final 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) { - switch($fk->getType()): - case Doctrine_Relation::ONE_COMPOSITE: - case Doctrine_Relation::MANY_COMPOSITE: - $local = $fk->getLocal(); - $foreign = $fk->getForeign(); - - if($record->getTable()->hasPrimaryKey($fk->getLocal())) { - switch($record->getState()): - case Doctrine_Record::STATE_TDIRTY: - case Doctrine_Record::STATE_TCLEAN: - $saveLater[$k] = $fk; - break; - case Doctrine_Record::STATE_CLEAN: - case Doctrine_Record::STATE_DIRTY: - $v->save(); - break; - endswitch; - } else { - // ONE-TO-ONE relationship - $obj = $record->get($fk->getTable()->getComponentName()); - - if($obj->getState() != Doctrine_Record::STATE_TCLEAN) - $obj->save(); - - } - break; - endswitch; - } elseif($fk instanceof Doctrine_Relation_Association) { - $v->save(); - } - } - return $saveLater; - } - /** - * updates the given record - * - * @param Doctrine_Record $record - * @return boolean - */ - private function update(Doctrine_Record $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; - - if(isset($this->validator)) { - if( ! $this->validator->validateRecord($record)) { - return false; - } - } - - $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->dbh->prepare($sql); - $stmt->execute($params); - - $record->assignIdentifier(true); - - return true; - } - /** - * inserts a record into database - * - * @param Doctrine_Record $record - * @return boolean - */ - private function insert(Doctrine_Record $record) { - $array = $record->getPrepared(); - - if(empty($array)) - return false; - - $seq = $record->getTable()->getSequenceName(); - - if( ! empty($seq)) { - $id = $this->getNextID($seq); - $name = $record->getTable()->getIdentifier(); - $array[$name] = $id; - } - - if(isset($this->validator)) { - if( ! $this->validator->validateRecord($record)) { - return false; - } - } - - $strfields = join(", ", array_keys($array)); - $strvalues = substr(str_repeat("?, ",count($array)),0,-2); - - $sql = "INSERT INTO ".$record->getTable()->getTableName()." (".$strfields.") VALUES (".$strvalues.")"; - - $stmt = $this->dbh->prepare($sql); - - $stmt->execute(array_values($array)); - - return true; - } - /** - * 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($record->getTable()->getAlias($fk->getTable()->getComponentName())); - $obj->delete(); - break; - endswitch; - } - } - /** - * deletes this data access object and all the related composites - * this operation is isolated by a transaction - * - * this event can be listened by the onPreDelete and onDelete listeners - * - * @return boolean true on success, false on failure - */ - final public function delete(Doctrine_Record $record) { - switch($record->getState()): - case Doctrine_Record::STATE_PROXY: - case Doctrine_Record::STATE_CLEAN: - case Doctrine_Record::STATE_DIRTY: - $this->beginTransaction(); - - $this->deleteComposites($record); - $this->addDelete($record); - - $this->commit(); - return true; - break; - default: - return false; - endswitch; - } - /** - * adds record into pending insert list - * @param Doctrine_Record $record - */ - public function addInsert(Doctrine_Record $record) { - $name = $record->getTable()->getComponentName(); - $this->insert[$name][] = $record; - } - /** - * adds record into penging update list - * @param Doctrine_Record $record - */ - public function addUpdate(Doctrine_Record $record) { - $name = $record->getTable()->getComponentName(); - $this->update[$name][] = $record; - } - /** - * adds record into pending delete list - * @param Doctrine_Record $record - */ - public function addDelete(Doctrine_Record $record) { - $name = $record->getTable()->getComponentName(); - $this->delete[$name][] = $record; - } - /** - * returns the pending insert list - * - * @return array - */ - public function getInserts() { - return $this->insert; - } - /** - * returns the pending update list - * - * @return array - */ - public function getUpdates() { - return $this->update; - } - /** - * returns the pending delete list - * - * @return array - */ - public function getDeletes() { - return $this->delete; - } - - /** - * returns a string representation of this object - * @return string - */ - public function __toString() { - return Doctrine_Lib::getSessionAsString($this); - } -} -