diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php index 0a0fda8e863f106551635030633bf0698c3f58b7..14b865cc89c2155bdef26d11dba1f740ed56fec4 100644 --- a/lib/Doctrine/ClassMetadata.php +++ b/lib/Doctrine/ClassMetadata.php @@ -141,20 +141,20 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable protected $_mappedEmbeddedValues = array(); /** - * An array of field names. used to look up field names from column names. - * Keys are column names and values are field names. - * This is the reverse lookup map of $_columnNames. + * Enter description here... * * @var array */ - protected $_fieldNames = array(); + protected $_attributes = array('loadReferences' => true); /** - * Enter description here... + * An array of field names. used to look up field names from column names. + * Keys are column names and values are field names. + * This is the reverse lookup map of $_columnNames. * - * @var unknown_type + * @var array */ - protected $_attributes = array('loadReferences' => true); + protected $_fieldNames = array(); /** * An array of column names. Keys are field names and values column names. @@ -165,12 +165,21 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable */ protected $_columnNames = array(); + /** + * Map that maps lowercased column names to field names. + * Mainly used during hydration because Doctrine enforces PDO_CASE_LOWER + * for portability. + * + * @var array + */ + protected $_lcColumnToFieldNames = array(); + /** * Enter description here... * * @var unknown_type */ - protected $_subclassFieldNames = array(); + //protected $_subclassFieldNames = array(); /** * Caches enum value mappings. Keys are field names and values arrays with the @@ -192,7 +201,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable * * @var integer */ - protected $_columnCount; + //protected $_columnCount; /** * Whether or not this class has default values. @@ -540,10 +549,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } /** - * getFieldName - * - * returns the field name for a column name - * if no field name can be found the column name is returned. + * Gets the field name for a column name. + * If no field name can be found the column name is returned. * * @param string $columnName column name * @return string column alias @@ -555,28 +562,57 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } /** - * + * Gets the field name for a completely lowercased column name. + * Mainly used during hydration. + * + * @param string $lcColumnName + * @return string */ - public function lookupFieldName($columnName) + public function getFieldNameForLowerColumnName($lcColumnName) { - if (isset($this->_fieldNames[$columnName])) { - return $this->_fieldNames[$columnName]; - } else if (isset($this->_subclassFieldNames[$columnName])) { - return $this->_subclassFieldNames[$columnName]; - } - - $classMetadata = $this; - $conn = $this->_em; + return isset($this->_lcColumnToFieldNames[$lcColumnName]) ? + $this->_lcColumnToFieldNames[$lcColumnName] : $lcColumnName; + } + + public function hasLowerColumn($lcColumnName) + { + return isset($this->_lcColumnToFieldNames[$lcColumnName]); + } + + /** + * Looks up the field name for a (lowercased) column name. + * + * This is mostly used during hydration, because we want to make the + * conversion to field names while iterating over the result set for best + * performance. By doing this at that point, we can avoid re-iterating over + * the data just to convert the column names to field names. + * + * However, when this is happening, we don't know the real + * class name to instantiate yet (the row data may target a sub-type), hence + * this method looks up the field name in the subclass mappings if it's not + * found on this class mapping. + * This lookup on subclasses is costly but happens only *once* for a column + * during hydration because the hydrator caches effectively. + */ + public function lookupFieldName($lcColumnName) + { + if (isset($this->_lcColumnToFieldNames[$lcColumnName])) { + return $this->_lcColumnToFieldNames[$lcColumnName]; + }/* else if (isset($this->_subclassFieldNames[$lcColumnName])) { + return $this->_subclassFieldNames[$lcColumnName]; + }*/ - foreach ($classMetadata->getSubclasses() as $subClass) { - $subClassMetadata = $conn->getClassMetadata($subClass); - if ($subClassMetadata->hasColumn($columnName)) { - $this->_subclassFieldNames[$columnName] = $subClassMetadata->getFieldName($columnName); - return $this->_subclassFieldNames[$columnName]; + foreach ($this->getSubclasses() as $subClass) { + $subClassMetadata = $this->_em->getClassMetadata($subClass); + if ($subClassMetadata->hasLowerColumn($lcColumnName)) { + /*$this->_subclassFieldNames[$lcColumnName] = $subClassMetadata-> + getFieldNameForLowerColumnName($lcColumnName); + return $this->_subclassFieldNames[$lcColumnName];*/ + return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName); } } - throw new Doctrine_ClassMetadata_Exception("No field name found for column name '$columnName' during lookup."); + throw new Doctrine_ClassMetadata_Exception("No field name found for column name '$lcColumnName' during lookup."); } /** @@ -615,26 +651,30 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } } - // extract column name & field name + // extract column name & field name & lowercased column name $parts = explode(' as ', $name); if (count($parts) > 1) { $fieldName = $parts[1]; } else { $fieldName = $parts[0]; } - $name = strtolower($parts[0]); + $columnName = $parts[0]; + $lcColumnName = strtolower($parts[0]); - if (isset($this->_columnNames[$fieldName])) { + if (isset($this->_mappedColumns[$columnName])) { return; } + // Fill column name <-> field name lookup maps if ($prepend) { - $this->_columnNames = array_merge(array($fieldName => $name), $this->_columnNames); - $this->_fieldNames = array_merge(array($name => $fieldName), $this->_fieldNames); + $this->_columnNames = array_merge(array($fieldName => $columnName), $this->_columnNames); + $this->_fieldNames = array_merge(array($columnName => $fieldName), $this->_fieldNames); } else { - $this->_columnNames[$fieldName] = $name; - $this->_fieldNames[$name] = $fieldName; + $this->_columnNames[$fieldName] = $columnName; + $this->_fieldNames[$columnName] = $fieldName; } + $this->_lcColumnToFieldNames[$lcColumnName] = $fieldName; + // Inspect & fill $options @@ -662,9 +702,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable }*/ if ($prepend) { - $this->_mappedColumns = array_merge(array($name => $options), $this->_mappedColumns); + $this->_mappedColumns = array_merge(array($columnName => $options), $this->_mappedColumns); } else { - $this->_mappedColumns[$name] = $options; + $this->_mappedColumns[$columnName] = $options; } $this->_columnCount++; diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index 9505f107e39a82f8452d2578eb709297bfd5d1a2..bdb22d39a5d906cc297ac8b631bac10b9868477c 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -65,7 +65,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator * * @var Doctrine_Entity */ - protected $reference; + protected $_owner; /** * The reference field of the collection. @@ -93,7 +93,14 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator * * @var Doctrine_Null */ - protected static $null; + //protected static $null; + + /** + * The EntityManager. + * + * @var EntityManager + */ + protected $_em; /** * Constructor. @@ -104,15 +111,12 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function __construct($entityBaseType, $keyField = null) { - if (is_string($entityBaseType)) { - $this->_entityBaseType = $entityBaseType; - $mapper = Doctrine_EntityManagerFactory::getManager($entityBaseType) - ->getEntityPersister($entityBaseType); - } - $this->_mapper = $mapper; + $this->_entityBaseType = $entityBaseType; + $this->_em = Doctrine_EntityManagerFactory::getManager($entityBaseType); + $this->_mapper = $this->_em->getEntityPersister($entityBaseType); if ($keyField === null) { - $keyField = $mapper->getClassMetadata()->getBoundQueryPart('indexBy'); + $keyField = $this->_mapper->getClassMetadata()->getBoundQueryPart('indexBy'); } if ($keyField === null) { @@ -127,39 +131,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } } - /** - * initNullObject - * Initializes the null object for this collection. - * - * @return void - */ - public static function initNullObject(Doctrine_Null $null) - { - self::$null = $null; - } - - /** - * getTable - * Returns the table of the mapper of the collection. - * - * @return Doctrine_Table - */ - public function getTable() - { - return $this->_mapper->getTable(); - } - - /** - * getMapper - * Returns the mapper of this collection. - * - * @return Doctrine_Mapper - */ - public function getMapper() - { - return $this->_mapper; - } - /** * setData * @@ -306,25 +277,25 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * setReference + * INTERNAL: * sets a reference pointer * * @return void */ - public function setReference(Doctrine_Entity $record, Doctrine_Relation $relation) + public function setReference(Doctrine_Entity $entity, Doctrine_Relation $relation) { - $this->reference = $record; + $this->_owner = $entity; $this->relation = $relation; if ($relation instanceof Doctrine_Relation_ForeignKey || $relation instanceof Doctrine_Relation_LocalKey) { $this->referenceField = $relation->getForeignFieldName(); - $value = $record->get($relation->getLocalFieldName()); - foreach ($this->data as $record) { + $value = $entity->get($relation->getLocalFieldName()); + foreach ($this->data as $entity) { if ($value !== null) { - $record->set($this->referenceField, $value, false); + $entity->set($this->referenceField, $value, false); } else { - $record->set($this->referenceField, $this->reference, false); + $entity->set($this->referenceField, $this->_owner, false); } } } else if ($relation instanceof Doctrine_Relation_Association) { @@ -333,18 +304,18 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** + * INTERNAL: * getReference * * @return mixed */ public function getReference() { - return $this->reference; + return $this->_owner; } /** - * remove - * removes a specified collection element + * Removes an entity from the collection. * * @param mixed $key * @return boolean @@ -357,8 +328,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * contains - * whether or not this collection contains a specified element + * Checks whether the collection contains an entity. * * @param mixed $key the key of the element * @return boolean @@ -378,17 +348,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * get * returns a record for given key * - * There are two special cases: - * - * 1. if null is given as a key a new record is created and attached - * at the end of the collection - * - * 2. if given key does not exist, then a new record is create and attached - * to the given key - * * Collection also maps referential information to newly created records * * @param mixed $key the key of the element @@ -396,33 +357,10 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function get($key) { - if ( ! isset($this->data[$key])) { - $record = $this->_mapper->create(); - - if (isset($this->referenceField)) { - $value = $this->reference->get($this->relation->getLocalFieldName()); - - if ($value !== null) { - $record->set($this->referenceField, $value, false); - } else { - $record->set($this->referenceField, $this->reference, false); - } - } - - if ($key === null) { - $this->data[] = $record; - } else { - $this->data[$key] = $record; - } - - if (isset($this->_keyField)) { - $record->set($this->_keyField, $key); - } - - return $record; + if (isset($this->data[$key])) { + return $this->data[$key]; } - - return $this->data[$key]; + return null; } /** @@ -491,16 +429,16 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator * @internal Can't type-hint the second parameter to Doctrine_Entity because we need * to adhere to the Doctrine_Access::set() signature. */ - public function set($key, $record) + public function set($key, $entity) { - if ( ! $record instanceOf Doctrine_Entity) { + if ( ! $entity instanceof Doctrine_Entity) { throw new Doctrine_Collection_Exception('Value variable in set is not an instance of Doctrine_Entity'); } if (isset($this->referenceField)) { - $record->set($this->referenceField, $this->reference, false); + $entity->set($this->referenceField, $this->_owner, false); } - $this->data[$key] = $record; + $this->data[$key] = $entity; } /** @@ -517,12 +455,12 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } if (isset($this->referenceField)) { - $value = $this->reference->get($this->relation->getLocalFieldName()); + $value = $this->_owner->get($this->relation->getLocalFieldName()); if ($value !== null) { $record->set($this->referenceField, $value, false); } else { - $record->set($this->referenceField, $this->reference, false); + $record->set($this->referenceField, $this->_owner, false); } } /* @@ -559,6 +497,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** + * INTERNAL: * loadRelated * * @param mixed $name @@ -578,8 +517,10 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $list[] = $value; } } - $query->from($this->_mapper->getComponentName() . '(' . implode(", ",$this->_mapper->getTable()->getPrimaryKeys()) . ')'); - $query->where($this->_mapper->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')'); + $query->from($this->_mapper->getComponentName() + . '(' . implode(", ",$this->_mapper->getTable()->getPrimaryKeys()) . ')'); + $query->where($this->_mapper->getComponentName() + . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')'); return $query; } @@ -607,6 +548,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** + * INTERNAL: * populateRelated * * @param string $name @@ -929,9 +871,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $this->data = array(); - if ($this->reference) { - $this->reference->free($deep); - $this->reference = null; + if ($this->_owner) { + $this->_owner->free($deep); + $this->_owner = null; } } diff --git a/lib/Doctrine/Configuration.php b/lib/Doctrine/Configuration.php index d5985a85c4e0baff1d4f54c741930e5927b1984b..c43ea6456e40a9e67e34ac05c4f385d5c0697a79 100644 --- a/lib/Doctrine/Configuration.php +++ b/lib/Doctrine/Configuration.php @@ -27,6 +27,7 @@ * The Configuration is the container for all configuration options of Doctrine. * It combines all configuration options from DBAL & ORM. * + * @author Roman Borschel <roman@code-factory.org> * @since 2.0 */ class Doctrine_Configuration diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 4a822dccdbbcbf4477d6ea4b1ecb3a51522394d6..ce091b850c1ee150ea803680ad85a2a63c92901c 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -78,10 +78,9 @@ abstract class Doctrine_Connection implements Countable /** * The PDO database handle. * - * @var PDO - * @todo Rename to $pdo. + * @var PDO */ - protected $dbh; + protected $_pdo; /** * The Configuration. @@ -116,14 +115,14 @@ abstract class Doctrine_Connection implements Countable * * @var string $driverName */ - protected $driverName; + protected $_driverName; /** * Whether or not a connection has been established. * * @var boolean $isConnected */ - protected $isConnected = false; + protected $_isConnected = false; /** * An array containing all features this driver supports, keys representing feature @@ -163,7 +162,7 @@ abstract class Doctrine_Connection implements Countable * * @var array $availableDrivers */ - private static $availableDrivers = array( + private static $_availableDrivers = array( 'Mysql', 'Pgsql', 'Oracle', 'Informix', 'Mssql', 'Sqlite', 'Firebird' ); @@ -172,7 +171,7 @@ abstract class Doctrine_Connection implements Countable * * @var integer */ - protected $_count = 0; + protected $_queryCount = 0; /* @@ -220,14 +219,13 @@ abstract class Doctrine_Connection implements Countable /** * Constructor. * - * @param Doctrine_Manager $manager the manager object - * @param PDO|Doctrine_Adapter_Interface $adapter database driver + * @param array $params The connection parameters. */ public function __construct(array $params) { if (isset($params['pdo'])) { - $this->dbh = $params['pdo']; - $this->isConnected = true; + $this->_pdo = $params['pdo']; + $this->_isConnected = true; } $this->_params = $params; } @@ -319,27 +317,36 @@ abstract class Doctrine_Connection implements Countable } /** - * getDriverName - * * Gets the name of the instance driver * * @return void */ public function getDriverName() { - return $this->driverName; + return $this->_driverName; } /** * returns the database handler which this connection uses * * @return PDO the database handler + * @deprecated */ public function getDbh() { - //$this->connect(); - - return $this->dbh; + $this->connect(); + return $this->_pdo; + } + + /** + * Gets the PDO handle used by the connection. + * + * @return PDO + */ + public function getPdo() + { + $this->connect(); + return $this->_pdo; } /** @@ -349,36 +356,43 @@ abstract class Doctrine_Connection implements Countable */ public function connect() { - if ($this->isConnected) { + if ($this->_isConnected) { return false; } //$event = new Doctrine_Event($this, Doctrine_Event::CONN_CONNECT); //$this->getListener()->preConnect($event); - $e = explode(':', $this->options['dsn']); + // TODO: the extension_loaded check can happen earlier, maybe in the factory if (extension_loaded('pdo')) { - if (in_array($e[0], PDO::getAvailableDrivers())) { - $this->dbh = new PDO( - $this->options['dsn'], $this->options['username'], - $this->options['password'], $this->options['other']); - $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); - } + $driverOptions = isset($this->_params['driverOptions']) ? + $this->_params['driverOptions'] : array(); + $user = isset($this->_params['user']) ? + $this->_params['user'] : null; + $password = isset($this->_params['password']) ? + $this->_params['password'] : null; + $this->_pdo = new PDO( + $this->_constructPdoDsn(), + $user, + $password, + $driverOptions + ); + $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); } else { throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]); } // attach the pending attributes to adapter - foreach($this->pendingAttributes as $attr => $value) { + /*foreach($this->pendingAttributes as $attr => $value) { // some drivers don't support setting this so we just skip it if ($attr == Doctrine::ATTR_DRIVER_NAME) { continue; } - $this->dbh->setAttribute($attr, $value); - } + $this->_pdo->setAttribute($attr, $value); + }*/ - $this->isConnected = true; + $this->_isConnected = true; //$this->getListener()->postConnect($event); return true; @@ -394,7 +408,7 @@ abstract class Doctrine_Connection implements Countable */ protected function _constructPdoDsn() { - + throw Doctrine_Exception::notImplemented('_constructPdoDsn', get_class($this)); } /** @@ -402,19 +416,11 @@ abstract class Doctrine_Connection implements Countable */ public function incrementQueryCount() { - $this->_count++; + $this->_queryCount++; } /** - * converts given driver name - * - * @param - */ - public function driverName($name) - {} - - /** - * supports + * Checks whether a certain feature is supported. * * @param string $feature the name of the feature * @return boolean whether or not this drivers supports given feature @@ -665,7 +671,7 @@ abstract class Doctrine_Connection implements Countable */ public function quote($input, $type = null) { - return $this->dbh->quote($input, $type); + return $this->_pdo->quote($input, $type); } /** @@ -782,7 +788,7 @@ abstract class Doctrine_Connection implements Countable $stmt = false; if ( ! $event->skipOperation) { - $stmt = $this->dbh->prepare($statement); + $stmt = $this->_pdo->prepare($statement); } $this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event); @@ -846,8 +852,8 @@ abstract class Doctrine_Connection implements Countable $this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event); if ( ! $event->skipOperation) { - $stmt = $this->dbh->query($query); - $this->_count++; + $stmt = $this->_pdo->query($query); + $this->_queryCount++; } $this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event); @@ -881,8 +887,8 @@ abstract class Doctrine_Connection implements Countable $this->getAttribute(Doctrine::ATTR_LISTENER)->preExec($event); if ( ! $event->skipOperation) { - $count = $this->dbh->exec($query); - $this->_count++; + $count = $this->_pdo->exec($query); + $this->_queryCount++; } $this->getAttribute(Doctrine::ATTR_LISTENER)->postExec($event); @@ -926,7 +932,7 @@ abstract class Doctrine_Connection implements Countable //$event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR); //$this->getListener()->preError($event); - $name = 'Doctrine_Connection_' . $this->driverName . '_Exception'; + $name = 'Doctrine_Connection_' . $this->_driverName . '_Exception'; $exc = new $name($e->getMessage(), (int) $e->getCode()); if ( ! is_array($e->errorInfo)) { @@ -949,7 +955,7 @@ abstract class Doctrine_Connection implements Countable */ public function count() { - return $this->_count; + return $this->_queryCount; } /** @@ -964,8 +970,8 @@ abstract class Doctrine_Connection implements Countable $this->clear(); - unset($this->dbh); - $this->isConnected = false; + unset($this->_pdo); + $this->_isConnected = false; //$this->getAttribute(Doctrine::ATTR_LISTENER)->postClose($event); } @@ -990,7 +996,7 @@ abstract class Doctrine_Connection implements Countable { $this->connect(); - return $this->dbh->errorCode(); + return $this->_pdo->errorCode(); } /** @@ -1003,7 +1009,7 @@ abstract class Doctrine_Connection implements Countable { $this->connect(); - return $this->dbh->errorInfo(); + return $this->_pdo->errorInfo(); } /** diff --git a/lib/Doctrine/Connection/Mock.php b/lib/Doctrine/Connection/Mock.php index 81f353c7fd728336c73ef61d3631aedea2bd4658..66a6f90d8ba798817fa334fbe0233c82aa5d6479 100644 --- a/lib/Doctrine/Connection/Mock.php +++ b/lib/Doctrine/Connection/Mock.php @@ -38,7 +38,7 @@ class Doctrine_Connection_Mock extends Doctrine_Connection_Common /** * @var string $driverName the name of this connection driver */ - protected $driverName = 'MySql'; + protected $_driverName = 'Mysql'; /** * the constructor diff --git a/lib/Doctrine/Connection/Mysql.php b/lib/Doctrine/Connection/Mysql.php index b01aed9a5f4975511c6bd6ea5d156a2d770d0b7a..c5f84a19542dc066ce0d9e3adb7e2c01992b3b54 100644 --- a/lib/Doctrine/Connection/Mysql.php +++ b/lib/Doctrine/Connection/Mysql.php @@ -40,7 +40,7 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common * * @var string */ - protected $driverName = 'Mysql'; + protected $_driverName = 'Mysql'; /** * the constructor @@ -206,5 +206,31 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common $query = 'REPLACE INTO ' . $tableName . ' (' . $query . ') VALUES (' . $values . ')'; return $this->exec($query); + } + + /** + * Constructs the MySql PDO DSN. + * + * Overrides Connection#_constructPdoDsn(). + * + * @return string The DSN. + */ + protected function _constructPdoDsn() + { + $dsn = 'mysql:'; + if (isset($this->_params['host'])) { + $dsn .= 'host=' . $this->_params['host'] . ';'; + } + if (isset($this->_params['port'])) { + $dsn .= 'port=' . $this->_params['port'] . ';'; + } + if (isset($this->_params['dbname'])) { + $dsn .= 'dbname=' . $this->_params['dbname'] . ';'; + } + if (isset($this->_params['unix_socket'])) { + $dsn .= 'unix_socket=' . $this->_params['unix_socket'] . ';'; + } + + return $dsn; } } diff --git a/lib/Doctrine/Connection/Sqlite.php b/lib/Doctrine/Connection/Sqlite.php index 8910ef8756a0e95695b4bc1f27e57733c3945ac9..a462ce025ab69c1a96550f82fe2739d3ddcf51b0 100644 --- a/lib/Doctrine/Connection/Sqlite.php +++ b/lib/Doctrine/Connection/Sqlite.php @@ -38,7 +38,7 @@ class Doctrine_Connection_Sqlite extends Doctrine_Connection_Common /** * @var string $driverName the name of this connection driver */ - protected $driverName = 'Sqlite'; + protected $_driverName = 'Sqlite'; /** * the constructor @@ -67,13 +67,14 @@ class Doctrine_Connection_Sqlite extends Doctrine_Connection_Common 'prepared_statements' => 'emulated', 'identifier_quoting' => true, 'pattern_escaping' => false, - ); + ); + parent::__construct($params); - if ($this->isConnected) { - $this->dbh->sqliteCreateFunction('mod', array('Doctrine_Expression_Sqlite', 'modImpl'), 2); - $this->dbh->sqliteCreateFunction('md5', 'md5', 1); - $this->dbh->sqliteCreateFunction('now', 'time', 0); + if ($this->_isConnected) { + $this->_pdo->sqliteCreateFunction('mod', array('Doctrine_Expression_Sqlite', 'modImpl'), 2); + $this->_pdo->sqliteCreateFunction('md5', 'md5', 1); + $this->_pdo->sqliteCreateFunction('now', 'time', 0); } } @@ -85,15 +86,15 @@ class Doctrine_Connection_Sqlite extends Doctrine_Connection_Common */ public function connect() { - if ($this->isConnected) { + if ($this->_isConnected) { return false; } parent::connect(); - $this->dbh->sqliteCreateFunction('mod', array('Doctrine_Expression_Sqlite', 'modImpl'), 2); - $this->dbh->sqliteCreateFunction('md5', 'md5', 1); - $this->dbh->sqliteCreateFunction('now', 'time', 0); + $this->_pdo->sqliteCreateFunction('mod', array('Doctrine_Expression_Sqlite', 'modImpl'), 2); + $this->_pdo->sqliteCreateFunction('md5', 'md5', 1); + $this->_pdo->sqliteCreateFunction('now', 'time', 0); } /** @@ -125,18 +126,37 @@ class Doctrine_Connection_Sqlite extends Doctrine_Connection_Common */ public function dropDatabase() { - try { - if ( ! $dsn = $this->getOption('dsn')) { - throw new Doctrine_Connection_Exception('You must create your Doctrine_Connection by using a valid Doctrine style dsn in order to use the create/drop database functionality'); - } - - $info = $this->getManager()->parseDsn($dsn); + try { + if ( ! $dsn = $this->getOption('dsn')) { + throw new Doctrine_Connection_Exception('You must create your Doctrine_Connection by using a valid Doctrine style dsn in order to use the create/drop database functionality'); + } - $this->export->dropDatabase($info['database']); + $info = $this->getManager()->parseDsn($dsn); - return 'Successfully dropped database for connection "' . $this->getName() . '" at path "' . $info['database'] . '"'; - } catch (Exception $e) { - return $e; - } + $this->export->dropDatabase($info['database']); + + return 'Successfully dropped database for connection "' . $this->getName() . '" at path "' . $info['database'] . '"'; + } catch (Exception $e) { + return $e; + } + } + + /** + * Constructs the Sqlite PDO DSN. + * + * Overrides Connection#_constructPdoDsn(). + * + * @return string The DSN. + */ + protected function _constructPdoDsn() + { + $dsn = 'sqlite:'; + if (isset($this->_params['path'])) { + $dsn .= $this->_params['path']; + } else if (isset($this->_params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; } } \ No newline at end of file diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php index 1590d49e63166f84c9cc8a3223c428662bfef004..9eb7a449408e456fc47719bd3330777b6a4ce2d3 100644 --- a/lib/Doctrine/Connection/UnitOfWork.php +++ b/lib/Doctrine/Connection/UnitOfWork.php @@ -22,17 +22,19 @@ #namespace Doctrine::ORM::Internal; /** - * The UnitOfWork is responsible for writing out changes to the database at - * the correct time and in the correct order. + * The UnitOfWork is responsible for tracking changes to objects during an + * "object-level" transaction and for writing out changes to the database at + * in the correct order. * * Some terminology: * * <b>New entity</b>: A new entity is an entity that already has an identity but * is not yet persisted into the database. This is usually the case for all - * newly saved entities that use a SEQUENCE id generator. Entities with an + * newly saved/persisted entities that use a SEQUENCE id generator. Entities with an * IDENTITY id generator get persisted as soon as they're saved in order to * obtain the identifier. Therefore entities that use an IDENTITY id generator * never appear in the list of new entities of the UoW. + * New entities are inserted into the database when the is UnitOfWork committed. * * <b>Dirty entity</b>: A dirty entity is a managed entity whose values have * been altered. @@ -53,7 +55,7 @@ * @author Roman Borschel <roman@code-factory.org> * @todo package:orm. Figure out a useful implementation. */ -class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module +class Doctrine_Connection_UnitOfWork { /** * The identity map that holds references to all managed entities that have @@ -94,6 +96,17 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module */ protected $_commitOrderCalculator; + /** + * Constructor. + * Created a new UnitOfWork. + * + * @param Doctrine_EntityManager $em + */ + public function __construct(Doctrine_EntityManager $em) + { + $this->_em = $em; + } + /** * Commits the unit of work, executing all operations that have been postponed * up to this point. @@ -201,7 +214,6 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module } /** - * buildFlushTree * builds a flush tree that is used in transactions * * The returned array has all the initialized components in @@ -301,14 +313,13 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module } /** - * saveAll * persists all the pending records from all tables * * @throws PDOException if something went wrong at database level * @return void * @deprecated */ - public function saveAll() + /*public function saveAll() { $this->conn->beginInternalTransaction(); // get the flush tree @@ -337,7 +348,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module } } $this->conn->commit(); - } + }*/ /** * Adds an entity to the pool of managed entities. diff --git a/lib/Doctrine/ConnectionFactory.php b/lib/Doctrine/ConnectionFactory.php index 0e3b6dd99445a16791620f042a1d1a8154491aa3..8f61cc243e670ef0d15e8d873aef5422dce71b3d 100644 --- a/lib/Doctrine/ConnectionFactory.php +++ b/lib/Doctrine/ConnectionFactory.php @@ -52,6 +52,12 @@ class Doctrine_ConnectionFactory } + /** + * Creates a connection object with the specified parameters. + * + * @param array $params + * @return Connection + */ public function createConnection(array $params) { // check for existing pdo object @@ -80,14 +86,6 @@ class Doctrine_ConnectionFactory if ( ! isset($params['driver'])) { throw Doctrine_ConnectionFactory_Exception::driverRequired(); } - // user - if ( ! isset($params['user'])) { - throw Doctrine_ConnectionFactory_Exception::userRequired(); - } - // password - if ( ! isset($params['password'])) { - throw Doctrine_ConnectionFactory_Exception::passwordRequired(); - } // check validity of parameters diff --git a/lib/Doctrine/EntityManager.php b/lib/Doctrine/EntityManager.php index c73d6747d38bcd3cfad71dd5ec190404eda19bb7..f00041b86ff21fcf5fd6d7e0819db043554ac9f4 100644 --- a/lib/Doctrine/EntityManager.php +++ b/lib/Doctrine/EntityManager.php @@ -148,7 +148,7 @@ class Doctrine_EntityManager $this->_name = $name; $this->_metadataFactory = new Doctrine_ClassMetadata_Factory( $this, new Doctrine_ClassMetadata_CodeDriver()); - $this->_unitOfWork = new Doctrine_Connection_UnitOfWork($conn); + $this->_unitOfWork = new Doctrine_Connection_UnitOfWork($this); $this->_nullObject = Doctrine_Null::$INSTANCE; } @@ -463,8 +463,8 @@ class Doctrine_EntityManager /** * Creates an entity. Used for reconstitution as well as initial creation. * - * @param - * @param + * @param string $className The name of the entity class. + * @param array $data The data for the entity. * @return Doctrine_Entity */ public function createEntity($className, array $data) @@ -485,22 +485,19 @@ class Doctrine_EntityManager } if ($isNew) { - $entity = new $className(true); - //$entity->_setData($data); + $entity = new $className; } else { $idHash = $this->_unitOfWork->getIdentifierHash($id); if ($entity = $this->_unitOfWork->tryGetByIdHash($idHash, $classMetadata->getRootClassName())) { return $entity; } else { - $entity = new $className(false); - //$entity->_setData($data); + $entity = new $className; $this->_unitOfWork->registerIdentity($entity); } } } else { - $entity = new $className(true); - //$entity->_setData($data); + $entity = new $className; } /*if (count($data) < $classMetadata->getMappedColumnCount()) { @@ -508,7 +505,6 @@ class Doctrine_EntityManager } else { $entity->_state(Doctrine_Entity::STATE_CLEAN); }*/ - $this->_tmpEntityData = array(); return $entity; } @@ -519,7 +515,9 @@ class Doctrine_EntityManager */ public function _getTmpEntityData() { - return $this->_tmpEntityData; + $data = $this->_tmpEntityData; + $this->_tmpEntityData = array(); + return $data; } /** @@ -528,7 +526,6 @@ class Doctrine_EntityManager * classname will be returned. * * @return string The name of the class to instantiate. - * @todo Can be optimized performance-wise. */ private function _inferCorrectClassName(array $data, $className) { @@ -553,10 +550,10 @@ class Doctrine_EntityManager * * @return UnitOfWork */ - public function getUnitOfWork() + /*public function getUnitOfWork() { return $this->_unitOfWork; - } + }*/ /** * Gets the EventManager used by the EntityManager. diff --git a/lib/Doctrine/EntityManagerFactory.php b/lib/Doctrine/EntityManagerFactory.php index 698f90289f687872896e56a550924530e3b0b1cd..72d371aca11a1c6b15c0771be49be6427c15e6e6 100644 --- a/lib/Doctrine/EntityManagerFactory.php +++ b/lib/Doctrine/EntityManagerFactory.php @@ -11,6 +11,7 @@ * instances as well as keeping track of all created EntityManagers and * hard bindings to Entities. * + * @author Roman Borschel <roman@code-factory.org> * @since 2.0 */ class Doctrine_EntityManagerFactory @@ -52,21 +53,44 @@ class Doctrine_EntityManagerFactory */ private $_config; + /** + * Constructor. + * Creates a new EntityManagerFactory. + */ public function __construct() { $this->_connFactory = new Doctrine_ConnectionFactory(); } + /** + * Sets the Configuration that is injected into all EntityManagers + * (and their Connections) that are created by this factory. + * + * @param Doctrine_Configuration $eventManager + */ public function setConfiguration(Doctrine_Configuration $config) { $this->_config = $config; } + /** + * Sets the EventManager that is injected into all EntityManagers + * (and their Connections) that are created by this factory. + * + * @param Doctrine_EventManager $eventManager + */ public function setEventManager(Doctrine_EventManager $eventManager) { $this->_eventManager = $eventManager; } + /** + * Creates an EntityManager. + * + * @param unknown_type $connParams + * @param unknown_type $name + * @return unknown + */ public function createEntityManager($connParams, $name = null) { if ( ! $this->_config) { diff --git a/lib/Doctrine/EntityPersister/Standard.php b/lib/Doctrine/EntityPersister/Standard.php index f7cec527cc1c81891a311ffdb767fa6a127ba1d9..da20b2326f412e6054d02f699a31a27f27b5a45f 100644 --- a/lib/Doctrine/EntityPersister/Standard.php +++ b/lib/Doctrine/EntityPersister/Standard.php @@ -20,7 +20,7 @@ */ /** - * The default mapping strategy maps a single entity instance to a single database table, + * The default persister strategy maps a single entity instance to a single database table, * as is the case in Single Table Inheritance & Concrete Table Inheritance. * * @author Roman Borschel <roman@code-factory.org> diff --git a/lib/Doctrine/EventListener.php b/lib/Doctrine/EventListener.php index 7543036424d2dc5e250a269961f02f5a534bf46c..7afc68f2162c14e429973de4fd305f3a70033001 100644 --- a/lib/Doctrine/EventListener.php +++ b/lib/Doctrine/EventListener.php @@ -30,7 +30,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @todo Remove. The 2.0 event system has no listener interfaces. */ class Doctrine_EventListener implements Doctrine_EventListener_Interface { diff --git a/lib/Doctrine/EventManager.php b/lib/Doctrine/EventManager.php index 11094a080cffcff758b8c2323700ea1432ba7518..1dee726f6f1bf9e59ed914714d15a002fdf2c167 100644 --- a/lib/Doctrine/EventManager.php +++ b/lib/Doctrine/EventManager.php @@ -21,12 +21,33 @@ #namespace Doctrine::Common; +/** + * The EventManager is the central point of Doctrine's event listener system. + * Listeners are registered on the manager and events are dispatch through the + * manager. + * + * @author Roman Borschel <roman@code-factory.org> + * @author Guilherme Blanco <guilhermeblanco@hotmail.com> + * @since 2.0 + */ class Doctrine_EventManager { + /** + * Map of registered listeners. + * <event> => <listeners> + * + * @var array + */ private $_listeners = array(); - - public function dispatchEvent($event) { + /** + * Dispatches an event to all registered listeners. + * + * @param string|Event $event The name of the event or the event object. + * @return boolean + */ + public function dispatchEvent($event) + { $argIsCallback = is_string($event); $callback = $argIsCallback ? $event : $event->getType(); @@ -40,25 +61,43 @@ class Doctrine_EventManager return ! $event->getDefaultPrevented(); } - - public function getListeners($callback = null) { - return $callback ? $this->_listeners[$callback] : $this->_listeners; + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $event The name of the event. + * @return + */ + public function getListeners($event = null) + { + return $event ? $this->_listeners[$event] : $this->_listeners; } - - public function hasListeners($callback) { - return isset($this->_listeners[$callback]); + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * @return boolean + */ + public function hasListeners($event) + { + return isset($this->_listeners[$event]); } - - public function addEventListener($callbacks, $listener) { + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param object $listener The listener object. + */ + public function addEventListener($events, $listener) + { // TODO: maybe check for duplicate registrations? - if ( ! is_array($callbacks)) { - $callbacks = array($callbacks); + if ( ! is_array($events)) { + $events = array($events); } - foreach ($callbacks as $callback) { - $this->_listeners[$callback] = $listener; + foreach ($events as $event) { + $this->_listeners[$event] = $listener; } } } diff --git a/lib/Doctrine/Exception.php b/lib/Doctrine/Exception.php index 950a25dc33c810df9f7319e486d8751f57ca2b08..7e52387d3344147c92b8a26f613a473d5f9caf57 100644 --- a/lib/Doctrine/Exception.php +++ b/lib/Doctrine/Exception.php @@ -46,6 +46,11 @@ class Doctrine_Exception extends Exception public function getInnerException() { return $this->_innerException; + } + + public static function notImplemented($method, $class) + { + return new self("The method '$method' is not implemented in the class '$class'."); } } diff --git a/lib/Doctrine/HydratorNew.php b/lib/Doctrine/HydratorNew.php index 80583247b476e53efc61c217fbec5f132cf0dbbe..78ab83d822c1808a672ffba371e4a7089e23d760 100644 --- a/lib/Doctrine/HydratorNew.php +++ b/lib/Doctrine/HydratorNew.php @@ -355,8 +355,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract foreach ($data as $key => $value) { // Parse each column name only once. Cache the results. if ( ! isset($cache[$key])) { - // check ignored names. fastest solution for now. if we get more we'll start - // to introduce a list. if ($this->_isIgnoredName($key)) continue; // cache general information like the column name <-> field name mapping @@ -439,8 +437,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract foreach ($data as $key => $value) { // Parse each column name only once. Cache the results. if ( ! isset($cache[$key])) { - // check ignored names. fastest solution for now. if we get more we'll start - // to introduce a list. if ($this->_isIgnoredName($key)) continue; // cache general information like the column name <-> field name mapping diff --git a/lib/Doctrine/Manager.php b/lib/Doctrine/Manager.php index 292315094c441cd82e27b5bde5c5b233f5319199..2adf187a3d40f9429e264530c1948ba4cc95cd13 100644 --- a/lib/Doctrine/Manager.php +++ b/lib/Doctrine/Manager.php @@ -30,7 +30,8 @@ * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen <kvesteri@cc.hut.fi> + * @author Konsta Vesterinen <kvesteri@cc.hut.fi> + * @todo Remove. */ class Doctrine_Manager implements Doctrine_Configurable, Countable, IteratorAggregate { diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 4413cade4a40c414153cd861d207765bc03404ab..3b6fc8932ac56cfc1547f7a177df8706d36b4008 100755 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -540,13 +540,14 @@ class Doctrine_Query extends Doctrine_Query_Abstract return $this; } - /** * Empty template method to provide Query subclasses with the possibility * to hook into the query building procedure, doing any custom / specialized * query building procedures that are neccessary. * * @return void + * @deprecated Should be removed. Extending Query is no good solution. Should + * Things like this should be done through listeners. */ public function preQuery() { @@ -559,12 +560,43 @@ class Doctrine_Query extends Doctrine_Query_Abstract * post query procedures (for example logging) that are neccessary. * * @return void + * @deprecated Should be removed. Extending Query is no good solution. Should + * Things like this should be done through listeners. */ public function postQuery() { } - + + /** + * Gets the list of results for the query. + * + * @param integer $hydrationMode + * @return mixed + */ + public function getResultList($hydrationMode = null) + { + return $this->execute(array(), $hydrationMode); + } + + /** + * Gets the single result of the query. + * Enforces the uniqueness of the result. If the result is not unique, + * a QueryException is thrown. + * + * @param integer $hydrationMode + * @return mixed + * @throws QueryException If the query result is not unique. + */ + public function getSingleResult($hydrationMode = null) + { + $result = $this->execute(array(), $hydrationMode); + if (count($result) > 1) { + throw Doctrine_Query_Exception::nonUniqueResult(); + } + + return is_array($result) ? array_shift($result) : $result->getFirst(); + } /** * This method is automatically called when this Doctrine_Hydrate is serialized. diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 3e0d327a92d0c96e099f4c80a5634fccff5cd938..b942b988be6e6b4f2b522afe033d1bc1332e80c8 100755 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -987,5 +987,28 @@ abstract class Doctrine_Query_Abstract * @return string SQL query */ abstract public function getSql(); + + /** + * Sets a query parameter. + * + * @param string|integer $key + * @param mixed $value + */ + public function setParameter($key, $value) + { + $this->_params[$key] = $value; + } + + /** + * Sets a collection of query parameters. + * + * @param array $params + */ + public function setParameters(array $params) + { + foreach ($params as $key => $value) { + $this->setParameter($key, $value); + } + } } diff --git a/lib/Doctrine/Query/Exception.php b/lib/Doctrine/Query/Exception.php index 362ac6a747fa0872ffce515db8e13e3b08cac2a6..2bcc833808c6cb5e85fccfa3b811e41cfd679567 100644 --- a/lib/Doctrine/Query/Exception.php +++ b/lib/Doctrine/Query/Exception.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * <http://www.phpdoctrine.org>. */ -Doctrine::autoload('Doctrine_Exception'); + /** * Doctrine_Query_Exception * @@ -31,4 +31,9 @@ Doctrine::autoload('Doctrine_Exception'); * @author Konsta Vesterinen <kvesteri@cc.hut.fi> */ class Doctrine_Query_Exception extends Doctrine_Exception -{ } \ No newline at end of file +{ + public static function nonUniqueResult() + { + return new self("The query contains more than one result."); + } +} diff --git a/lib/Doctrine/Sequence.php b/lib/Doctrine/Sequence.php index 34e8b3965f316aa7d2df86cf56eab2a2ea3fde91..f826624d2740ad1c880eb244236e9440a94d3456 100644 --- a/lib/Doctrine/Sequence.php +++ b/lib/Doctrine/Sequence.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * <http://www.phpdoctrine.org>. */ -Doctrine::autoload('Doctrine_Connection_Module'); + /** * Doctrine_Sequence * The base class for sequence handling drivers. diff --git a/lib/Doctrine/Transaction.php b/lib/Doctrine/Transaction.php index 144dec405512a648b8224f6236f47c99afebc323..b3a7f7fd59e02158b5bb13eacb257d610b2fb700 100644 --- a/lib/Doctrine/Transaction.php +++ b/lib/Doctrine/Transaction.php @@ -22,7 +22,6 @@ #namespace Doctrine::DBAL::Transactions; /** - * Doctrine_Transaction * Handles transaction savepoint and isolation abstraction * * @author Konsta Vesterinen <kvesteri@cc.hut.fi> diff --git a/tests/Orm/Query/DqlGenerationTest.php b/tests/Orm/Query/DqlGenerationTest.php index 959a4c40916bb6c086c21c0d0b6e7e24e253be1d..31e91a033b0e3368167bbd3c77de24cabdd3b85c 100755 --- a/tests/Orm/Query/DqlGenerationTest.php +++ b/tests/Orm/Query/DqlGenerationTest.php @@ -29,7 +29,7 @@ * @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://www.phpdoctrine.org - * @since 1.0 + * @since 2.0 * @version $Revision$ */ class Orm_Query_DqlGenerationTest extends Doctrine_OrmTestCase diff --git a/tests/Orm/UnitOfWorkTest.php b/tests/Orm/UnitOfWorkTest.php index cd26f6a5611102fd5395bab73d634a33347784de..354446d50bd88441160bb80c911e84c49f4f52a3 100644 --- a/tests/Orm/UnitOfWorkTest.php +++ b/tests/Orm/UnitOfWorkTest.php @@ -9,7 +9,7 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase protected function setUp() { parent::setUp(); $this->_user = new ForumUser(); - $this->_unitOfWork = $this->_em->getUnitOfWork(); + $this->_unitOfWork = new Doctrine_Connection_UnitOfWork($this->_em); } protected function tearDown() { diff --git a/tests/lib/Doctrine_TestUtil.php b/tests/lib/Doctrine_TestUtil.php index 9bdd47976287a90d6700a4450fc4977974209172..9a3b17b3166adb50cdf50d06df9222a420f18a59 100644 --- a/tests/lib/Doctrine_TestUtil.php +++ b/tests/lib/Doctrine_TestUtil.php @@ -19,7 +19,8 @@ class Doctrine_TestUtil //return Doctrine_Manager::connection($dsn, 'testconn'); } else { $params = array( - 'pdo' => new PDO('sqlite::memory:') + 'driver' => 'sqlite', + 'memory' => true ); }