diff --git a/lib/Doctrine.php b/lib/Doctrine.php index e848343f8..682d73576 100644 --- a/lib/Doctrine.php +++ b/lib/Doctrine.php @@ -33,6 +33,49 @@ * @version $Revision$ */ final class Doctrine { + /** + * ERROR CONSTANTS + */ + const ERR = -1; + const ERR_SYNTAX = -2; + const ERR_CONSTRAINT = -3; + const ERR_NOT_FOUND = -4; + const ERR_ALREADY_EXISTS = -5; + const ERR_UNSUPPORTED = -6; + const ERR_MISMATCH = -7; + const ERR_INVALID = -8; + const ERR_NOT_CAPABLE = -9; + const ERR_TRUNCATED = -10; + const ERR_INVALID_NUMBER = -11; + const ERR_INVALID_DATE = -12; + const ERR_DIVZERO = -13; + const ERR_NODBSELECTED = -14; + const ERR_CANNOT_CREATE = -15; + const ERR_CANNOT_DELETE = -16; + const ERR_CANNOT_DROP = -17; + const ERR_NOSUCHTABLE = -18; + const ERR_NOSUCHFIELD = -19; + const ERR_NEED_MORE_DATA = -20; + const ERR_NOT_LOCKED = -21; + const ERR_VALUE_COUNT_ON_ROW = -22; + const ERR_INVALID_DSN = -23; + const ERR_CONNECT_FAILED = -24; + const ERR_EXTENSION_NOT_FOUND = -25; + const ERR_NOSUCHDB = -26; + const ERR_ACCESS_VIOLATION = -27; + const ERR_CANNOT_REPLACE = -28; + const ERR_CONSTRAINT_NOT_NULL = -29; + const ERR_DEADLOCK = -30; + const ERR_CANNOT_ALTER = -31; + const ERR_MANAGER = -32; + const ERR_MANAGER_PARSE = -33; + const ERR_LOADMODULE = -34; + const ERR_INSUFFICIENT_DATA = -35; + /** + * class naming error + */ + const ERR_CLASS_NAME = -36; + /** * ATTRIBUTE CONSTANTS */ @@ -353,12 +396,12 @@ final class Doctrine { * @return boolean */ public static function autoload($classname) { - if(! self::$path) - self::$path = dirname(__FILE__); - if(class_exists($classname)) return false; + if(! self::$path) + self::$path = dirname(__FILE__); + $class = self::$path.DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR,$classname) . '.php'; if( ! file_exists($class)) @@ -366,6 +409,7 @@ final class Doctrine { require_once($class); + return true; } /** @@ -394,7 +438,7 @@ final class Doctrine { */ public static function isValidClassname($classname) { if(preg_match('~(^[a-z])|(_[a-z])|([\W])|(_{2})~', $classname)) - throw new Doctrine_Exception("Class name is not valid. Use camel case and underscores (i.e My_PerfectClass)."); + return false; return true; } diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 6cfb573ac..df4498bbb 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -597,20 +597,64 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun return $this->dbh->query($query); } /** + * execute * @param string $query sql query * @param array $params query parameters * - * @return PDOStatement + * @return PDOStatement|Doctrine_Adapter_Statement */ 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); + try { + if( ! empty($params)) { + $stmt = $this->dbh->prepare($query); + $stmt->execute($params); + return $stmt; + } else { + return $this->dbh->query($query); + } + } catch(Doctrine_Adapter_Exception $e) { + $this->rethrowException($e); + } catch(PDOException $e) { + $this->rethrowException($e); } } + /** + * exec + * @param string $query sql query + * @param array $params query parameters + * + * @return PDOStatement|Doctrine_Adapter_Statement + */ + public function exec($query, array $params = array()) { + try { + if( ! empty($params)) { + $stmt = $this->dbh->prepare($query); + $stmt->execute($params); + return $stmt; + } else { + return $this->dbh->exec($query); + } + } catch(Doctrine_Adapter_Exception $e) { + } catch(PDOException $e) { } + + $this->rethrowException($e); + } + /** + * rethrowException + * + * @throws Doctrine_Connection_Exception + */ + private function rethrowException(Exception $e) { + $name = 'Doctrine_Connection_' . $this->driverName . '_Exception'; + + $exc = new $name($e->getMessage(), (int) $e->getCode()); + if( ! is_array($e->errorInfo)) + $e->errorInfo = array(); + + $exc->errorInfo = $exc->processErrorInfo($e->errorInfo); + + throw $exc; + } /** * hasTable * whether or not this connection has table $name initialized diff --git a/lib/Doctrine/Connection/Exception.php b/lib/Doctrine/Connection/Exception.php index bfa7424df..e59ba84ad 100644 --- a/lib/Doctrine/Connection/Exception.php +++ b/lib/Doctrine/Connection/Exception.php @@ -68,7 +68,6 @@ class Doctrine_Connection_Exception extends Doctrine_Exception { Doctrine::ERR_TRUNCATED => 'truncated', Doctrine::ERR_DEADLOCK => 'deadlock detected', ); - /** * Return a textual error message for a Doctrine error code * diff --git a/lib/Doctrine/Export.php b/lib/Doctrine/Export.php index 567c7aba5..aaac6ef55 100644 --- a/lib/Doctrine/Export.php +++ b/lib/Doctrine/Export.php @@ -51,7 +51,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { * @return void */ public function dropTable($table) { - $this->conn->getDbh()->query('DROP TABLE ' . $table); + $this->conn->execute('DROP TABLE ' . $table); } /** @@ -63,7 +63,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { */ public function dropIndex($table, $name) { $name = $this->conn->quoteIdentifier($this->conn->getIndexName($name), true); - return $this->conn->getDbh()->exec('DROP INDEX ' . $name); + return $this->conn->exec('DROP INDEX ' . $name); } /** * drop existing constraint @@ -76,7 +76,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { public function dropConstraint($table, $name, $primary = false) { $table = $this->conn->quoteIdentifier($table, true); $name = $this->conn->quoteIdentifier($this->conn->getIndexName($name), true); - return $this->conn->getDbh()->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name); + return $this->conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name); } /** * drop existing sequence @@ -142,7 +142,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { $name = $this->conn->quoteIdentifier($name, true); $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; - return $this->conn->getDbh()->exec($query); + return $this->conn->exec($query); } /** * create sequence @@ -191,7 +191,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { $fields[] = $this->conn->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; - return $this->conn->getDbh()->exec($query); + return $this->conn->exec($query); } /** * Get the stucture of a field into an array @@ -226,7 +226,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { * @return void */ public function createIndex($table, $name, array $definition) { - return $this->conn->getDbh()->query($this->createIndexSql($table, $name, $definition)); + return $this->conn->execute($this->createIndexSql($table, $name, $definition)); } /** * Get the stucture of a field into an array @@ -363,7 +363,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { * @return void */ public function alterTable($name, array $changes, $check) { - $this->conn->getDbh()->query($this->alterTableSql($name, $changes, $check)); + $this->conn->execute($this->alterTableSql($name, $changes, $check)); } /** * alter an existing table @@ -589,7 +589,7 @@ class Doctrine_Export extends Doctrine_Connection_Module { * * @return void */ - public static function export() { + public static function exportAll() { $parent = new ReflectionClass('Doctrine_Record'); $conn = Doctrine_Manager::getInstance()->getCurrentConnection(); $old = $conn->getAttribute(Doctrine::ATTR_CREATE_TABLES); @@ -604,4 +604,40 @@ class Doctrine_Export extends Doctrine_Connection_Module { } $conn->setAttribute(Doctrine::ATTR_CREATE_TABLES, $old); } + public function export($record) { + if( ! $record instanceof Doctrine_Record) + $record = new $record(); + + $table = $record->getTable(); + + $reporter = new Doctrine_Reporter(); + + if( ! Doctrine::isValidClassname($table->getComponentName())) { + $reporter->add(E_WARNING, Doctrine::ERR_CLASS_NAME); + } + + try { + $columns = array(); + foreach($table->getColumns() as $name => $column) { + $definition = $column[2]; + $definition['type'] = $column[0]; + $definition['length'] = $column[1]; + + if($definition['type'] == 'enum' && isset($definition['default'])) + $definition['default'] = $table->enumIndex($name, $definition['default']); + + if($definition['type'] == 'boolean' && isset($definition['default'])) + $definition['default'] = (int) $definition['default']; + + $columns[$name] = $definition; + } + $this->createTable($table->getTableName(), $columns); + + } catch(Doctrine_Connection_Exception $e) { + + $reporter->add(E_ERROR, $e->getCode()); + } + + return $reporter; + } } diff --git a/lib/Doctrine/Export/Firebird.php b/lib/Doctrine/Export/Firebird.php index 70a1fb2c8..ca2c19d2f 100644 --- a/lib/Doctrine/Export/Firebird.php +++ b/lib/Doctrine/Export/Firebird.php @@ -89,7 +89,7 @@ class Doctrine_Export_Firebird extends Doctrine_Export { IF (NEW.' . $name . ' IS NULL OR NEW.' . $name . ' = 0) THEN NEW.' . $name . ' = GEN_ID('.$sequence_name.', 1); END'; - $result = $this->conn->getDbh()->exec($triggerSql); + $result = $this->conn->exec($triggerSql); // TODO ? $this->_silentCommit(); @@ -114,7 +114,7 @@ class Doctrine_Export_Firebird extends Doctrine_Export { //remove autoincrement trigger associated with the table $table = $this->conn->getDbh()->quote(strtoupper($table)); $trigger_name = $this->conn->getDbh()->quote(strtoupper($table) . '_AUTOINCREMENT_PK'); - $result = $this->conn->getDbh()->exec("DELETE FROM RDB\$TRIGGERS WHERE UPPER(RDB\$RELATION_NAME)=$table AND UPPER(RDB\$TRIGGER_NAME)=$trigger_name"); + $result = $this->conn->exec("DELETE FROM RDB\$TRIGGERS WHERE UPPER(RDB\$RELATION_NAME)=$table AND UPPER(RDB\$TRIGGER_NAME)=$trigger_name"); /** if (PEAR::isError($result)) { @@ -441,7 +441,7 @@ class Doctrine_Export_Firebird extends Doctrine_Export { } $query .= ' ('.implode(', ', $fields) . ')'; - $result = $this->conn->getDbh()->exec($query); + $result = $this->conn->exec($query); // todo: $this->_silentCommit(); return $result; } @@ -489,7 +489,7 @@ class Doctrine_Export_Firebird extends Doctrine_Export { $fields[] = $this->conn->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; - $result = $this->conn->getDbh()->exec($query); + $result = $this->conn->exec($query); // TODO ? $this->_silentCommit(); return $result; } @@ -503,9 +503,9 @@ class Doctrine_Export_Firebird extends Doctrine_Export { public function createSequence($seqName, $start = 1) { $sequenceName = $this->conn->getSequenceName($seqName); - $this->conn->getDbh()->exec('CREATE GENERATOR ' . $sequenceName); + $this->conn->exec('CREATE GENERATOR ' . $sequenceName); - $this->conn->getDbh()->exec('SET GENERATOR ' . $sequenceName . ' TO ' . ($start-1)); + $this->conn->exec('SET GENERATOR ' . $sequenceName . ' TO ' . ($start-1)); $this->dropSequence($seqName); } @@ -519,6 +519,6 @@ class Doctrine_Export_Firebird extends Doctrine_Export { $sequence_name = $this->conn->getSequenceName($seq_name); $sequence_name = $this->conn->getDbh()->quote($sequence_name); $query = "DELETE FROM RDB\$GENERATORS WHERE UPPER(RDB\$GENERATOR_NAME)=$sequence_name"; - return $this->conn->getDbh()->exec($query); + return $this->conn->exec($query); } } diff --git a/lib/Doctrine/Export/Mssql.php b/lib/Doctrine/Export/Mssql.php index 07edf801c..8ee4a4768 100644 --- a/lib/Doctrine/Export/Mssql.php +++ b/lib/Doctrine/Export/Mssql.php @@ -158,22 +158,18 @@ class Doctrine_Export_Mssql extends Doctrine_Export { case 'rename': case 'change': default: - return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, + return $db->raiseError(Doctrine::ERR_CANNOT_ALTER, null, null, 'alterTable: change type "'.$change_name.'" not yet supported'); } } - if ($check) { - return MDB2_OK; - } - $query = ''; if (!empty($changes['add']) && is_array($changes['add'])) { foreach ($changes['add'] as $field_name => $field) { if ($query) { $query.= ', '; } - $query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field); + $query.= 'ADD ' . $this->conn->getDeclaration($field['type'], $field_name, $field); } } @@ -182,7 +178,7 @@ class Doctrine_Export_Mssql extends Doctrine_Export { if ($query) { $query.= ', '; } - $field_name = $db->quoteIdentifier($field_name, true); + $field_name = $this->conn->quoteIdentifier($field_name, true); $query.= 'DROP COLUMN ' . $field_name; } } @@ -191,8 +187,8 @@ class Doctrine_Export_Mssql extends Doctrine_Export { return MDB2_OK; } - $name = $db->quoteIdentifier($name, true); - return $db->exec("ALTER TABLE $name $query"); + $name = $this->conn->quoteIdentifier($name, true); + return $this->conn->exec("ALTER TABLE $name $query"); } /** * create sequence @@ -235,6 +231,6 @@ class Doctrine_Export_Mssql extends Doctrine_Export { */ public function dropSequence($seqName) { $sequenceName = $db->quoteIdentifier($db->getSequenceName($seqName), true); - return $db->exec('DROP TABLE ' . $sequenceName); + return $this->conn->exec('DROP TABLE ' . $sequenceName); } } diff --git a/lib/Doctrine/Export/Mysql.php b/lib/Doctrine/Export/Mysql.php index 90bf6fd76..6b60f6a4b 100644 --- a/lib/Doctrine/Export/Mysql.php +++ b/lib/Doctrine/Export/Mysql.php @@ -41,7 +41,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { */ public function createDatabase($name) { $query = 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name, true); - $result = $this->conn->getDbh()->query($query); + $result = $this->conn->exec($query); } /** * drop an existing database @@ -52,7 +52,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { */ public function dropDatabase($name) { $query = 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); - $this->conn->getDbh()->query($query); + $this->conn->exec($query); } /** * create a new table @@ -132,7 +132,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { if (!empty($optionStrings)) { $query.= ' '.implode(' ', $optionStrings); } - return $this->conn->getDbh()->query($query); + return $this->conn->exec($query); } /** * alter an existing table @@ -392,7 +392,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { } $query .= ' ('. implode(', ', $fields) . ')'; - return $this->conn->getDbh()->query($query); + return $this->conn->exec($query); } /** * drop existing index @@ -404,7 +404,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { public function dropIndex($table, $name) { $table = $this->conn->quoteIdentifier($table, true); $name = $this->conn->quoteIdentifier($this->conn->getIndexName($name), true); - return $this->conn->getDbh()->query('DROP INDEX ' . $name . ' ON ' . $table); + return $this->conn->exec('DROP INDEX ' . $name . ' ON ' . $table); } /** * dropTable @@ -415,7 +415,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export { */ public function dropTable($table) { $table = $this->conn->quoteIdentifier($table, true); - $this->conn->getDbh()->query('DROP TABLE ' . $table); + $this->conn->exec('DROP TABLE ' . $table); } } ?> diff --git a/lib/Doctrine/Export/Oracle.php b/lib/Doctrine/Export/Oracle.php index f22acebfb..fae7ee4ae 100644 --- a/lib/Doctrine/Export/Oracle.php +++ b/lib/Doctrine/Export/Oracle.php @@ -152,7 +152,7 @@ BEGIN END IF; END; '; - return $this->conn->getDbh()->exec($trigger_sql); + return $this->conn->exec($trigger_sql); } /** * drop an existing autoincrement sequence + trigger @@ -173,7 +173,7 @@ END; $trigger_sql = 'DROP TRIGGER ' . $trigger_name; // if throws exception, trigger for autoincrement PK could not be dropped - $this->conn->getDbh()->exec($trigger_sql); + $this->conn->exec($trigger_sql); // if throws exception, sequence for autoincrement PK could not be dropped $this->dropSequence($table); @@ -417,7 +417,7 @@ END; $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); $query = 'CREATE SEQUENCE ' . $sequenceName . ' START WITH ' . $start . ' INCREMENT BY 1 NOCACHE'; $query.= ($start < 1 ? ' MINVALUE ' . $start : ''); - return $this->conn->getDbh()->exec($query); + return $this->conn->exec($query); } /** * drop existing sequence @@ -428,6 +428,6 @@ END; */ public function dropSequence($seqName) { $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - return $this->conn->getDbh()->exec('DROP SEQUENCE ' . $sequenceName); + return $this->conn->exec('DROP SEQUENCE ' . $sequenceName); } } diff --git a/lib/Doctrine/Export/Pgsql.php b/lib/Doctrine/Export/Pgsql.php index 61ba6f5aa..af889c697 100644 --- a/lib/Doctrine/Export/Pgsql.php +++ b/lib/Doctrine/Export/Pgsql.php @@ -41,7 +41,7 @@ class Doctrine_Export_Pgsql extends Doctrine_Export { */ public function createDatabase($name) { $query = 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name); - $this->conn->getDbh()->query($query); + $this->conn->exec($query); } /** * drop an existing database @@ -52,7 +52,7 @@ class Doctrine_Export_Pgsql extends Doctrine_Export { */ public function dropDatabase($name) { $query = 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); - $this->conn->getDbh()->query($query); + $this->conn->exec($query); } /** * alter an existing table diff --git a/lib/Doctrine/Export/Sqlite.php b/lib/Doctrine/Export/Sqlite.php index b67a21a75..89a717796 100644 --- a/lib/Doctrine/Export/Sqlite.php +++ b/lib/Doctrine/Export/Sqlite.php @@ -84,6 +84,6 @@ class Doctrine_Export_Sqlite extends Doctrine_Export { $fields[] = $fieldString; } $query .= ' ('.implode(', ', $fields) . ')'; - return $this->conn->getDbh()->exec($query); + return $this->conn->exec($query); } } diff --git a/lib/Doctrine/Hydrate.php b/lib/Doctrine/Hydrate.php index eb3fd4919..af5ffb731 100644 --- a/lib/Doctrine/Hydrate.php +++ b/lib/Doctrine/Hydrate.php @@ -387,31 +387,46 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { } if ( !isset($this->tables[$key]) ) - throw new Doctrine_Exception("No table named $key found."); + throw new Doctrine_Exception('No table named ' . $key . ' found.'); $ids = $this->tables[$key]->getIdentifier(); $name = $key; if($this->isIdentifiable($row, $ids)) { + if($name !== $root) { + $prev = $this->initRelated($prev, $name); + } + // aggregate values have numeric keys + if(isset($row[0])) { + $component = $this->tables[$name]->getComponentName(); + + // if the collection already has objects, get the last object + // otherwise create a new one where the aggregate values are being mapped - $prev = $this->initRelated($prev, $name); - // aggregate values have numeric keys - if(isset($row[0])) { - $path = array_search($name, $this->tableAliases); - $alias = $this->getPathAlias($path); + if($prev[$name]->count() > 0) { + $record = $prev[$name]->getLast(); + } else { + $record = new $component(); + $prev[$name]->add($record); + } - // map each aggregate value - foreach($row as $index => $value) { - $agg = false; + $path = array_search($name, $this->tableAliases); + $alias = $this->getPathAlias($path); - if(isset($this->pendingAggregates[$alias][$index])) - $agg = $this->pendingAggregates[$alias][$index][3]; + // map each aggregate value + foreach($row as $index => $value) { + $agg = false; - $prev[$name]->setAggregateValue($agg, $value); + if(isset($this->pendingAggregates[$alias][$index])) { + $agg = $this->pendingAggregates[$alias][$index][3]; } + $record->mapValue($agg, $value); } + } + continue; + } @@ -419,13 +434,28 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { $previd[$name] = array(); if($previd[$name] !== $row) { - // set internal data + // set internal data - $this->tables[$name]->setData($row); + $this->tables[$name]->setData($row); - // initialize a new record - $record = $this->tables[$name]->getRecord(); + // initialize a new record + $record = $this->tables[$name]->getRecord(); + // aggregate values have numeric keys + if(isset($row[0])) { + $path = array_search($name, $this->tableAliases); + $alias = $this->getPathAlias($path); + + // map each aggregate value + foreach($row as $index => $value) { + $agg = false; + + if(isset($this->pendingAggregates[$alias][$index])) + $agg = $this->pendingAggregates[$alias][$index][3]; + + $record->mapValue($agg, $value); + } + } if($name == $root) { @@ -462,7 +492,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { public function initRelated(array $prev, $name) { $pointer = $this->joins[$name]; $path = array_search($name, $this->tableAliases); - $tmp = explode(".", $path); + $tmp = explode('.', $path); $alias = end($tmp); if( ! isset($prev[$pointer]) ) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index fa24a8dc6..87a9ff0b4 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -283,31 +283,40 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable { * addFrom * * @param strint $from + * @return Doctrine_Query */ public function addFrom($from) { $class = 'Doctrine_Query_From'; $parser = new $class($this); $parser->parse($from); + + return $this; } /** * leftJoin * * @param strint $join + * @return Doctrine_Query */ public function leftJoin($join) { $class = 'Doctrine_Query_From'; $parser = new $class($this); - $parser->parse('LEFT JOIN '. $join); + $parser->parse('LEFT JOIN ' . $join); + + return $this; } /** * innerJoin * * @param strint $join + * @return Doctrine_Query */ public function innerJoin($join) { $class = 'Doctrine_Query_From'; $parser = new $class($this); - $parser->parse('INNER JOIN '. $join); + $parser->parse('INNER JOIN ' . $join); + + return $this; } /** * addWhere diff --git a/lib/Doctrine/Query/From.php b/lib/Doctrine/Query/From.php index d6c688bc1..57058ef36 100644 --- a/lib/Doctrine/Query/From.php +++ b/lib/Doctrine/Query/From.php @@ -15,23 +15,36 @@ class Doctrine_Query_From extends Doctrine_Query_Part { $parts = Doctrine_Query::bracketExplode($str, 'JOIN'); $operator = false; + + switch(trim($parts[0])) { + case 'INNER': + $operator = ':'; + case 'LEFT': + array_shift($parts); + } + $last = ''; foreach($parts as $k => $part) { $part = trim($part); - $e = explode(" ", $part); + + if(empty($part)) { + continue; + } + + $e = explode(' ', $part); if(end($e) == 'INNER' || end($e) == 'LEFT') $last = array_pop($e); - $part = implode(" ", $e); + $part = implode(' ', $e); foreach(Doctrine_Query::bracketExplode($part, ',') as $reference) { $reference = trim($reference); $e = explode('.', $reference); if($operator) { - $reference = array_shift($e).$operator.implode('.', $e); + $reference = array_shift($e) . $operator . implode('.', $e); } $table = $this->query->load($reference); } diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 4975f40c9..0e1f7df8b 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -74,24 +74,28 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * rather than the values of these protected variables */ /** - * @var object Doctrine_Table $table the factory that created this data access object + * @var object Doctrine_Table $_table the factory that created this data access object */ protected $_table; /** - * @var integer $id the primary keys of this object + * @var integer $_id the primary keys of this object */ protected $_id = array(); /** - * @var array $data the record data + * @var array $_data the record data */ protected $_data = array(); /** - * @var integer $state the state of this record + * @var array $_values the values array, aggregate values and such are mapped into this array + */ + protected $_values = array(); + /** + * @var integer $_state the state of this record * @see STATE_* constants */ protected $_state; /** - * @var array $modified an array containing properties that have been modified + * @var array $_modified an array containing properties that have been modified */ protected $_modified = array(); /** @@ -723,6 +727,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if($name === $this->_table->getIdentifier()) return null; + if(isset($this->_values[$lower])) + return $this->_values[$lower]; + $rel = $this->_table->getRelation($name); try { @@ -734,7 +741,20 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite return $this->references[$name]; } - + /** + * mapValue + * This simple method is used for mapping values to $values property. + * Usually this method is used internally by Doctrine for the mapping of + * aggregate values. + * + * @param string $name the name of the mapped value + * @param mixed $value mixed value to be mapped + * @return void + */ + public function mapValue($name, $value) { + $name = strtolower($name); + $this->_values[$name] = $value; + } /** * set * method for altering properties and Doctrine_Record references diff --git a/lib/Doctrine/Table.php b/lib/Doctrine/Table.php index 6ec1df527..88daba505 100644 --- a/lib/Doctrine/Table.php +++ b/lib/Doctrine/Table.php @@ -239,17 +239,16 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable { } } endswitch; - - if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) { + + if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) { if(Doctrine::isValidClassname($class->getName())) { - //$dict = new Doctrine_DataDict($this->getConnection()->getDBH()); try { $columns = array(); foreach($this->columns as $name => $column) { $definition = $column[2]; $definition['type'] = $column[0]; $definition['length'] = $column[1]; - + if($definition['type'] == 'enum' && isset($definition['default'])) $definition['default'] = $this->enumIndex($name, $definition['default']); @@ -265,6 +264,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable { } } + } } else { throw new Doctrine_Table_Exception("Class '$name' has no table definition."); diff --git a/manual/docs/Schema reference - Data types - Type modifiers.php b/manual/docs/Schema reference - Data types - Type modifiers.php index 2e6cdf82a..f9796c975 100644 --- a/manual/docs/Schema reference - Data types - Type modifiers.php +++ b/manual/docs/Schema reference - Data types - Type modifiers.php @@ -29,7 +29,6 @@ Building upon the above, we can say that the modifiers alter the field definitio

Using the above example, we can also explore the default field operator. Default is set in the same way as the notnull operator to set a default value for the field. This value may be set in any character set that the DBMS supports for text fields, and any other valid data for the field's data type. In the above example, we have specified a valid time for the "Time" data type, '12:34:05'. Remember that when setting default dates and times, as well as datetimes, you should research and stay within the epoch of your chosen DBMS, otherwise you will encounter difficult to diagnose errors!

-Example 33-1. Example of the length modifier
-The above example will create a character varying field of length 12 characters in the database table. If the length definition is left out, MDB2 will create a length of the maximum allowable length for the data type specified, which may create a problem with some field types and indexing. Best practice is to define lengths for all or most of your fields. +The above example will create a character varying field of length 12 characters in the database table. If the length definition is left out, Doctrine will create a length of the maximum allowable length for the data type specified, which may create a problem with some field types and indexing. Best practice is to define lengths for all or most of your fields. diff --git a/manual/documentation.php b/manual/documentation.php index aa0e1ddb9..46d10ee39 100644 --- a/manual/documentation.php +++ b/manual/documentation.php @@ -141,6 +141,7 @@ $menu = array('Getting started' => 'Enum', 'Gzip', ), +/** 'Column attributes' => array( 'Introduction', 'Primary', @@ -170,7 +171,7 @@ $menu = array('Getting started' => 'Autoincremented', 'Natural', 'Composite', - 'Sequential') + 'Sequential')*/ ), 'Basic Components' => diff --git a/tests/DriverTestCase.php b/tests/DriverTestCase.php index a9afa4ab3..31ac0816c 100644 --- a/tests/DriverTestCase.php +++ b/tests/DriverTestCase.php @@ -4,6 +4,7 @@ class AdapterMock implements Doctrine_Adapter_Interface { private $queries = array(); + private $exception = array(); public function __construct($name) { $this->name = $name; @@ -14,13 +15,25 @@ class AdapterMock implements Doctrine_Adapter_Interface { public function pop() { return array_pop($this->queries); } - + public function forceException($name, $message, $code) { + $this->exception = array($name, $message, $code); + } public function prepare($prepareString){ return new AdapterStatementMock; } public function query($queryString) { $this->queries[] = $queryString; - + + $e = $this->exception; + + if( ! empty($e)) { + $name = $e[0]; + + $this->exception = array(); + + throw new $name($e[1], $e[2]); + } + return new AdapterStatementMock; } public function getAll() { @@ -31,7 +44,17 @@ class AdapterMock implements Doctrine_Adapter_Interface { } public function exec($statement) { $this->queries[] = $statement; - + + $e = $this->exception; + + if( ! empty($e)) { + $name = $e[0]; + + $this->exception = array(); + + throw new $name($e[1], $e[2]); + } + return 0; } public function lastInsertId(){ } @@ -105,7 +128,7 @@ class Doctrine_Driver_UnitTestCase extends UnitTestCase { $this->transaction = new $tx($this->conn); if(class_exists($dataDict)) { $this->dataDict = new $dataDict($this->conn); - } + } //$this->dataDict = $this->conn->dataDict; } else { $this->export = new Doctrine_Export($this->conn); diff --git a/tests/ExportSqliteTestCase.php b/tests/ExportSqliteTestCase.php index af09cb313..267f2973d 100644 --- a/tests/ExportSqliteTestCase.php +++ b/tests/ExportSqliteTestCase.php @@ -3,6 +3,16 @@ class Doctrine_Export_Sqlite_TestCase extends Doctrine_Driver_UnitTestCase { public function __construct() { parent::__construct('sqlite'); } + public function testExportDoesntWorkWithExistingTable() { + $this->manager->setAttribute(Doctrine::ATTR_CREATE_TABLES, false); + $this->adapter->forceException('PDOException', 'table already exist', 123); + + $reporter = $this->export->export('User'); + + // Class name is not valid. Double underscores are not allowed + + $this->assertEqual($reporter->pop(), array(E_ERROR, Doctrine::ERR_ALREADY_EXISTS)); + } public function testCreateDatabaseDoesNotExecuteSql() { try { $this->export->createDatabase('db'); diff --git a/tests/ExportTestCase.php b/tests/ExportTestCase.php index d67fa7a68..07d76d6a2 100644 --- a/tests/ExportTestCase.php +++ b/tests/ExportTestCase.php @@ -1,6 +1,6 @@ assertEqual($q->getQuery(), 'SELECT e.id AS e__id, e.name AS e__name FROM "entity" e INNER JOIN "phonenumber" p ON e.id = p.entity_id WHERE e.id IN (SELECT DISTINCT e2.id FROM "entity" e2 INNER JOIN "phonenumber" p2 ON e2.id = p2.entity_id WHERE (e2.type = 0) LIMIT 5) AND (e.type = 0)'); - $this->connection->setAttribute(Doctrine::ATTR_QUOTE_IDENTIFIER, false); + $this->connection->setAttribute(Doctrine::ATTR_QUOTE_IDENTIFIER, false); } } diff --git a/tests/QuerySelectTestCase.php b/tests/QuerySelectTestCase.php index 153d262a2..4fd110aff 100644 --- a/tests/QuerySelectTestCase.php +++ b/tests/QuerySelectTestCase.php @@ -1,6 +1,6 @@ parseQuery('SELECT u.id, COUNT(p.id) FROM User u, u.Phonenumber p GROUP BY u.id'); $users = $q->execute(); - - $this->assertEqual($users[0]->Phonenumber->getAggregateValue('COUNT'), 1); - $this->assertEqual($users[1]->Phonenumber->getAggregateValue('COUNT'), 3); - $this->assertEqual($users[2]->Phonenumber->getAggregateValue('COUNT'), 1); - $this->assertEqual($users[3]->Phonenumber->getAggregateValue('COUNT'), 1); - $this->assertEqual($users[4]->Phonenumber->getAggregateValue('COUNT'), 3); + + $this->assertEqual($users[0]->Phonenumber[0]->COUNT, 1); + $this->assertEqual($users[1]->Phonenumber[0]->COUNT, 3); + $this->assertEqual($users[2]->Phonenumber[0]->COUNT, 1); + $this->assertEqual($users[3]->Phonenumber[0]->COUNT, 1); + $this->assertEqual($users[4]->Phonenumber[0]->COUNT, 3); + } public function testAggregateFunctionValueHydrationWithAliases() { @@ -73,14 +74,33 @@ class Doctrine_Query_Select_TestCase extends Doctrine_UnitTestCase { $q->parseQuery('SELECT u.id, COUNT(p.id) count FROM User u, u.Phonenumber p GROUP BY u.id'); $users = $q->execute(); - - $this->assertEqual($users[0]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[1]->Phonenumber->getAggregateValue('count'), 3); - $this->assertEqual($users[2]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[3]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[4]->Phonenumber->getAggregateValue('count'), 3); + + $this->assertEqual($users[0]->Phonenumber[0]->count, 1); + $this->assertEqual($users[1]->Phonenumber[0]->count, 3); + $this->assertEqual($users[2]->Phonenumber[0]->count, 1); + $this->assertEqual($users[3]->Phonenumber[0]->count, 1); + $this->assertEqual($users[4]->Phonenumber[0]->count, 3); } public function testMultipleAggregateFunctionValueHydrationWithAliases() { + $q = new Doctrine_Query(); + + $q->parseQuery('SELECT u.id, COUNT(p.id) count, MAX(p.phonenumber) max FROM User u, u.Phonenumber p GROUP BY u.id'); + + $users = $q->execute(); + $this->assertEqual($users[0]->Phonenumber[0]->count, 1); + $this->assertEqual($users[1]->Phonenumber[0]->count, 3); + $this->assertEqual($users[2]->Phonenumber[0]->count, 1); + $this->assertEqual($users[3]->Phonenumber[0]->count, 1); + $this->assertEqual($users[4]->Phonenumber[0]->count, 3); + + $this->assertEqual($users[0]->Phonenumber[0]->max, '123 123'); + $this->assertEqual($users[1]->Phonenumber[0]->max, '789 789'); + $this->assertEqual($users[2]->Phonenumber[0]->max, '123 123'); + $this->assertEqual($users[3]->Phonenumber[0]->max, '111 222 333'); + $this->assertEqual($users[4]->Phonenumber[0]->max, '444 555'); + } + public function testMultipleAggregateFunctionValueHydrationWithAliasesAndCleanRecords() { + $this->connection->clear(); $q = new Doctrine_Query(); @@ -88,18 +108,21 @@ class Doctrine_Query_Select_TestCase extends Doctrine_UnitTestCase { $users = $q->execute(); - $this->assertEqual($users[0]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[1]->Phonenumber->getAggregateValue('count'), 3); - $this->assertEqual($users[2]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[3]->Phonenumber->getAggregateValue('count'), 1); - $this->assertEqual($users[4]->Phonenumber->getAggregateValue('count'), 3); + $this->assertEqual($users[0]->Phonenumber[0]->state(), Doctrine_Record::STATE_TDIRTY); - $this->assertEqual($users[0]->Phonenumber->getAggregateValue('max'), '123 123'); - $this->assertEqual($users[1]->Phonenumber->getAggregateValue('max'), '789 789'); - $this->assertEqual($users[2]->Phonenumber->getAggregateValue('max'), '123 123'); - $this->assertEqual($users[3]->Phonenumber->getAggregateValue('max'), '111 222 333'); - $this->assertEqual($users[4]->Phonenumber->getAggregateValue('max'), '444 555'); + $this->assertEqual($users[0]->Phonenumber[0]->count, 1); + $this->assertEqual($users[1]->Phonenumber[0]->count, 3); + $this->assertEqual($users[2]->Phonenumber[0]->count, 1); + $this->assertEqual($users[3]->Phonenumber[0]->count, 1); + $this->assertEqual($users[4]->Phonenumber[0]->count, 3); + + $this->assertEqual($users[0]->Phonenumber[0]->max, '123 123'); + $this->assertEqual($users[1]->Phonenumber[0]->max, '789 789'); + $this->assertEqual($users[2]->Phonenumber[0]->max, '123 123'); + $this->assertEqual($users[3]->Phonenumber[0]->max, '111 222 333'); + $this->assertEqual($users[4]->Phonenumber[0]->max, '444 555'); } + public function testSingleComponentWithAsterisk() { $q = new Doctrine_Query(); diff --git a/tests/QueryUpdateTestCase.php b/tests/QueryUpdateTestCase.php index 956d22063..777b93d85 100644 --- a/tests/QueryUpdateTestCase.php +++ b/tests/QueryUpdateTestCase.php @@ -5,13 +5,26 @@ class Doctrine_Query_Update_TestCase extends Doctrine_UnitTestCase { $q->parseQuery("UPDATE User u SET u.name = 'someone'"); - $this->assertEqual($q->getQuery(), "UPDATE entity SET e.name = 'someone' WHERE (e.type = 0)"); + $this->assertEqual($q->getQuery(), "UPDATE entity e SET e.name = 'someone' WHERE (e.type = 0)"); $q = new Doctrine_Query(); - $q->update('User u')->set('u.name', 'someone'); + $q->update('User u')->set('u.name', "'someone'"); - $this->assertEqual($q->getQuery(), "UPDATE entity SET e.name = 'someone' WHERE (e.type = 0)"); + $this->assertEqual($q->getQuery(), "UPDATE entity e SET e.name = 'someone' WHERE (e.type = 0)"); + } + public function testUpdateWorksWithMultipleColumns() { + $q = new Doctrine_Query(); + + $q->parseQuery("UPDATE User u SET u.name = 'someone', u.email_id = 5"); + + $this->assertEqual($q->getQuery(), "UPDATE entity e SET e.name = 'someone', e.email_id = 5 WHERE (e.type = 0)"); + + $q = new Doctrine_Query(); + + $q->update('User u')->set('u.name', "'someone'")->set('u.email_id', 5); + + $this->assertEqual($q->getQuery(), "UPDATE entity e SET e.name = 'someone', e.email_id = 5 WHERE (e.type = 0)"); } } ?> diff --git a/tests/run.php b/tests/run.php index b56f22008..6f8b5fcc3 100644 --- a/tests/run.php +++ b/tests/run.php @@ -44,6 +44,7 @@ require_once('QueryShortAliasesTestCase.php'); require_once('QueryDeleteTestCase.php'); require_once('QueryUpdateTestCase.php'); require_once('QueryIdentifierQuotingTestCase.php'); +require_once('QueryAggregateValueTestCase.php'); require_once('UnitOfWorkTestCase.php'); @@ -101,6 +102,7 @@ class Doctrine_Tester { } */ require_once('ExportTestCase.php'); +require_once('ExportReporterTestCase.php'); require_once('ExportMysqlTestCase.php'); require_once('ExportFirebirdTestCase.php'); require_once('ExportPgsqlTestCase.php'); @@ -147,10 +149,14 @@ $test->addTestCase(new Doctrine_Export_Firebird_TestCase()); $test->addTestCase(new Doctrine_Configurable_TestCase()); - */ +$test->addTestCase(new Doctrine_Export_Sqlite_TestCase()); + + */ +$test->addTestCase(new Doctrine_Export_Reporter_TestCase()); + $test->addTestCase(new Doctrine_Transaction_TestCase()); $test->addTestCase(new Doctrine_Transaction_Mysql_TestCase()); @@ -187,8 +193,6 @@ $test->addTestCase(new Doctrine_Query_MultiJoin_TestCase()); $test->addTestCase(new Doctrine_Record_TestCase()); - - $test->addTestCase(new Doctrine_Relation_TestCase()); $test->addTestCase(new Doctrine_Record_State_TestCase()); @@ -197,7 +201,6 @@ $test->addTestCase(new Doctrine_Record_State_TestCase()); $test->addTestCase(new Doctrine_SchemaTestCase()); - $test->addTestCase(new Doctrine_EventListenerTestCase()); $test->addTestCase(new Doctrine_Connection_Transaction_TestCase()); @@ -210,10 +213,8 @@ $test->addTestCase(new Doctrine_BatchIteratorTestCase()); //$test->addTestCase(new Doctrine_Collection_Offset_TestCase()); - $test->addTestCase(new Doctrine_ViewTestCase()); - $test->addTestCase(new Doctrine_CustomPrimaryKeyTestCase()); $test->addTestCase(new Doctrine_Filter_TestCase()); @@ -232,7 +233,6 @@ $test->addTestCase(new Doctrine_RelationAccessTestCase()); $test->addTestCase(new Doctrine_CustomResultSetOrderTestCase()); - //$test->addTestCase(new Doctrine_Record_Filter_TestCase()); $test->addTestCase(new Doctrine_Query_Condition_TestCase()); @@ -251,16 +251,18 @@ $test->addTestCase(new Doctrine_Query_From_TestCase()); $test->addTestCase(new Doctrine_Query_Delete_TestCase()); -$test->addTestCase(new Doctrine_Query_Update_TestCase()); - $test->addTestCase(new Doctrine_Query_Where_TestCase()); $test->addTestCase(new Doctrine_Query_Limit_TestCase()); -$test->addTestCase(new Doctrine_Query_Select_TestCase()); - $test->addTestCase(new Doctrine_Query_IdentifierQuoting_TestCase()); +$test->addTestCase(new Doctrine_Query_Update_TestCase()); + +$test->addTestCase(new Doctrine_Query_AggregateValue_TestCase()); + + +$test->addTestCase(new Doctrine_Query_Select_TestCase()); //$test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());