From 537c8e4951d1908fad375b4d613d4a74253d117d Mon Sep 17 00:00:00 2001 From: romanb Date: Mon, 22 Jun 2009 18:48:42 +0000 Subject: [PATCH] [2.0] Implemented DQL bulk DELETE support for Class Table Inheritance. Other cleanups, refactorings and docblock additions. --- build.properties.dev | 3 +- build.xml | 52 ++- lib/Doctrine/DBAL/Connection.php | 24 +- lib/Doctrine/DBAL/Driver.php | 8 +- lib/Doctrine/DBAL/Driver/Connection.php | 22 +- lib/Doctrine/DBAL/Driver/PDOConnection.php | 2 +- lib/Doctrine/DBAL/Driver/PDOStatement.php | 2 +- lib/Doctrine/DBAL/Driver/Statement.php | 36 +- lib/Doctrine/DBAL/DriverManager.php | 7 +- .../DBAL/Platforms/AbstractPlatform.php | 20 +- lib/Doctrine/DBAL/Statement.php | 438 ------------------ lib/Doctrine/DBAL/Types/BooleanType.php | 8 +- lib/Doctrine/DBAL/Types/DateTimeType.php | 8 +- lib/Doctrine/DBAL/Types/DateType.php | 8 +- lib/Doctrine/DBAL/Types/TimeType.php | 8 +- lib/Doctrine/DBAL/Types/Type.php | 51 +- lib/Doctrine/ORM/EntityManager.php | 10 +- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 10 + .../Persisters/StandardEntityPersister.php | 18 +- .../ORM/Query/Exec/AbstractExecutor.php | 23 +- .../Query/Exec/MultiTableDeleteExecutor.php | 87 +++- lib/Doctrine/ORM/Query/SqlWalker.php | 30 ++ lib/Doctrine/ORM/UnitOfWork.php | 13 +- .../Functional/ClassTableInheritanceTest.php | 7 + 24 files changed, 321 insertions(+), 574 deletions(-) delete mode 100644 lib/Doctrine/DBAL/Statement.php diff --git a/build.properties.dev b/build.properties.dev index 18c4fe202..d3a6b1835 100644 --- a/build.properties.dev +++ b/build.properties.dev @@ -1,3 +1,4 @@ version=2.0 build.dir=build -dist.dir=dir \ No newline at end of file +dist.dir=dist +report.dir=reports \ No newline at end of file diff --git a/build.xml b/build.xml index 9fef9518f..dded9f630 100644 --- a/build.xml +++ b/build.xml @@ -8,20 +8,32 @@ + + + + @@ -29,32 +41,38 @@ + - + - + + + - + - + + - + @@ -62,9 +80,15 @@ - + - + + + + + @@ -76,14 +100,20 @@ + - + + @@ -92,14 +122,16 @@ + - - diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 86760a281..1d7208506 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -35,23 +35,6 @@ use Doctrine\Common\DoctrineException; * @author Konsta Vesterinen * @author Lukas Smith (MDB2 library) * @author Roman Borschel - * @todo - * 1) REPLICATION SUPPORT - * Replication support should be tackled at this layer (DBAL). - * There can be options that look like: - * 'slaves' => array( - * 'slave1' => array( - * user, pass etc. - * ), - * 'slave2' => array( - * user, pass etc. - * )), - * 'slaveConnectionResolver' => new MySlaveConnectionResolver(), - * 'masters' => array(...), - * 'masterConnectionResolver' => new MyMasterConnectionResolver() - * - * Doctrine\DBAL could ship with a simple standard broker that uses a primitive - * round-robin approach to distribution. User can provide its own brokers. */ class Connection { @@ -451,9 +434,9 @@ class Connection $params = array_merge(array_values($data), array_values($identifier)); $sql = 'UPDATE ' . $this->quoteIdentifier($tableName) - . ' SET ' . implode(', ', $set) - . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) - . ' = ?'; + . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; return $this->exec($sql, $params); } @@ -528,7 +511,6 @@ class Connection */ public function quote($input, $type = null) { - $this->connect(); return $this->_conn->quote($input, $type); } diff --git a/lib/Doctrine/DBAL/Driver.php b/lib/Doctrine/DBAL/Driver.php index 829ee330b..06a235882 100644 --- a/lib/Doctrine/DBAL/Driver.php +++ b/lib/Doctrine/DBAL/Driver.php @@ -17,7 +17,7 @@ interface Driver * @param string $username The username to use when connecting. * @param string $password The password to use when connecting. * @param array $driverOptions The driver options to use when connecting. - * @return Doctrine::DBAL::Connection The database connection. + * @return Doctrine\DBAL\Driver\Connection The database connection. */ public function connect(array $params, $username = null, $password = null, array $driverOptions = array()); @@ -25,7 +25,7 @@ interface Driver * Gets the DatabasePlatform instance that provides all the metadata about * the platform this driver connects to. * - * @return Doctrine::DBAL::DatabasePlatform The database platform. + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. */ public function getDatabasePlatform(); @@ -39,9 +39,9 @@ interface Driver public function getSchemaManager(Connection $conn); /** - * Get the name of the driver + * Gets the name of the driver. * - * @return string The name of the driver + * @return string The name of the driver. */ public function getName(); diff --git a/lib/Doctrine/DBAL/Driver/Connection.php b/lib/Doctrine/DBAL/Driver/Connection.php index 4bff3ca0a..1a9d431d1 100644 --- a/lib/Doctrine/DBAL/Driver/Connection.php +++ b/lib/Doctrine/DBAL/Driver/Connection.php @@ -23,7 +23,7 @@ namespace Doctrine\DBAL\Driver; /** * Connection interface. - * Drivers must implement this interface. + * Driver connections must implement this interface. * * This resembles the PDO interface. * @@ -31,14 +31,14 @@ namespace Doctrine\DBAL\Driver; */ interface Connection { - public function prepare($prepareString); - public function query(); - public function quote($input); - public function exec($statement); - public function lastInsertId(); - public function beginTransaction(); - public function commit(); - public function rollBack(); - public function errorCode(); - public function errorInfo(); + function prepare($prepareString); + function query(); + function quote($input); + function exec($statement); + function lastInsertId(); + function beginTransaction(); + function commit(); + function rollBack(); + function errorCode(); + function errorInfo(); } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Driver/PDOConnection.php b/lib/Doctrine/DBAL/Driver/PDOConnection.php index 55f11b65b..be6fddfa1 100644 --- a/lib/Doctrine/DBAL/Driver/PDOConnection.php +++ b/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -29,7 +29,7 @@ use \PDO; * * @since 2.0 */ -class PDOConnection extends PDO implements \Doctrine\DBAL\Driver\Connection +class PDOConnection extends PDO implements Connection { public function __construct($dsn, $user = null, $password = null, array $options = null) { diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php index 15baf451d..50b1e211a 100644 --- a/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -27,7 +27,7 @@ namespace Doctrine\DBAL\Driver; * * @since 2.0 */ -class PDOStatement extends \PDOStatement implements \Doctrine\DBAL\Driver\Statement +class PDOStatement extends \PDOStatement implements Statement { private function __construct() {} } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Driver/Statement.php b/lib/Doctrine/DBAL/Driver/Statement.php index 9d0d79e17..5982a563d 100644 --- a/lib/Doctrine/DBAL/Driver/Statement.php +++ b/lib/Doctrine/DBAL/Driver/Statement.php @@ -46,7 +46,7 @@ interface Statement * @param integer $type Data type of the parameter, specified by the PDO::PARAM_* constants. * @return boolean Returns TRUE on success or FALSE on failure */ - public function bindColumn($column, &$param, $type = null); + function bindColumn($column, &$param, $type = null); /** * Binds a value to a corresponding named or positional @@ -61,7 +61,7 @@ interface Statement * * @return boolean Returns TRUE on success or FALSE on failure. */ - public function bindValue($param, $value, $type = null); + function bindValue($param, $value, $type = null); /** * Binds a PHP variable to a corresponding named or question mark placeholder in the @@ -89,7 +89,7 @@ interface Statement * @param mixed $driverOptions * @return boolean Returns TRUE on success or FALSE on failure. */ - public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array()); + function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array()); /** * closeCursor @@ -97,7 +97,7 @@ interface Statement * * @return boolean Returns TRUE on success or FALSE on failure. */ - public function closeCursor(); + function closeCursor(); /** * columnCount @@ -107,7 +107,7 @@ interface Statement * by the PDOStatement object. If there is no result set, * this method should return 0. */ - public function columnCount(); + function columnCount(); /** * errorCode @@ -116,7 +116,7 @@ interface Statement * @see Doctrine_Adapter_Interface::errorCode() * @return string error code string */ - public function errorCode(); + function errorCode(); /** * errorInfo @@ -125,7 +125,7 @@ interface Statement * @see Doctrine_Adapter_Interface::errorInfo() * @return array error info array */ - public function errorInfo(); + function errorInfo(); /** * Executes a prepared statement @@ -141,7 +141,7 @@ interface Statement * bound parameters in the SQL statement being executed. * @return boolean Returns TRUE on success or FALSE on failure. */ - public function execute($params = null); + function execute($params = null); /** * fetch @@ -170,7 +170,7 @@ interface Statement * * @return mixed */ - public function fetch($fetchStyle = Query::HYDRATE_BOTH, + function fetch($fetchStyle = Query::HYDRATE_BOTH, $cursorOrientation = Query::HYDRATE_ORI_NEXT, $cursorOffset = null); @@ -187,7 +187,7 @@ interface Statement * * @return array */ - public function fetchAll($fetchStyle = Query::HYDRATE_BOTH); + function fetchAll($fetchStyle = Query::HYDRATE_BOTH); /** * fetchColumn @@ -200,7 +200,7 @@ interface Statement * * @return string returns a single column in the next row of a result set. */ - public function fetchColumn($columnIndex = 0); + function fetchColumn($columnIndex = 0); /** * fetchObject @@ -215,7 +215,7 @@ interface Statement * @return mixed an instance of the required class with property names that correspond * to the column names or FALSE in case of an error. */ - public function fetchObject($className = 'stdClass', $args = array()); + function fetchObject($className = 'stdClass', $args = array()); /** * getAttribute @@ -225,7 +225,7 @@ interface Statement * @see Doctrine::ATTR_* constants * @return mixed the attribute value */ - public function getAttribute($attribute); + function getAttribute($attribute); /** * getColumnMeta @@ -243,7 +243,7 @@ interface Statement * precision The numeric precision of this column. Normally 0 for types other than floating point decimals. * pdo_type The type of this column as represented by the PDO::PARAM_* constants. */ - public function getColumnMeta($column); + function getColumnMeta($column); /** * nextRowset @@ -256,7 +256,7 @@ interface Statement * * @return boolean Returns TRUE on success or FALSE on failure. */ - public function nextRowset(); + function nextRowset(); /** * rowCount @@ -270,7 +270,7 @@ interface Statement * * @return integer Returns the number of rows. */ - public function rowCount(); + function rowCount(); /** * setAttribute @@ -280,7 +280,7 @@ interface Statement * @param mixed $value the value of given attribute * @return boolean Returns TRUE on success or FALSE on failure. */ - public function setAttribute($attribute, $value); + function setAttribute($attribute, $value); /** * setFetchMode @@ -289,5 +289,5 @@ interface Statement * @param integer $mode The fetch mode must be one of the Query::HYDRATE_* constants. * @return boolean Returns 1 on success or FALSE on failure. */ - public function setFetchMode($mode, $arg1); + function setFetchMode($mode, $arg1); } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 2ff9433aa..105241ae0 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -94,7 +94,8 @@ final class DriverManager * @param Doctrine\Common\EventManager The event manager to use. * @return Doctrine\DBAL\Connection */ - public static function getConnection(array $params, + public static function getConnection( + array $params, Configuration $config = null, EventManager $eventManager = null) { @@ -107,10 +108,10 @@ final class DriverManager } // check for existing pdo object - if (isset($params['pdo']) && ! $params['pdo'] instanceof PDO) { + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { throw DoctrineException::invalidPDOInstance(); } else if (isset($params['pdo'])) { - $params['driver'] = $params['pdo']->getAttribute(PDO::ATTR_DRIVER_NAME); + $params['driver'] = $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); } else { self::_checkParams($params); } diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index e2c576c6d..e724d0308 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -1504,6 +1504,10 @@ abstract class AbstractPlatform } /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime value of this platform. + * + * @return string The format string. * TODO: We need to get the specific format for each dbms and override this * function for each platform */ @@ -1512,11 +1516,23 @@ abstract class AbstractPlatform return 'Y-m-d H:i:s'; } + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored date value of this platform. + * + * @return string The format string. + */ public function getDateFormatString() { return 'Y-m-d'; } - + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored time value of this platform. + * + * @return string The format string. + */ public function getTimeFormatString() { return 'H:i:s'; @@ -1535,7 +1551,7 @@ abstract class AbstractPlatform } /** - * Get the platform name for this instance + * Gets the name of the platform. * * @return string */ diff --git a/lib/Doctrine/DBAL/Statement.php b/lib/Doctrine/DBAL/Statement.php deleted file mode 100644 index a6686f7ac..000000000 --- a/lib/Doctrine/DBAL/Statement.php +++ /dev/null @@ -1,438 +0,0 @@ -. - */ - -namespace Doctrine\DBAL; - -/** - * A thin wrapper around PDOStatement. - * - * @author Konsta Vesterinen - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.org - * @since 1.0 - * @version $Revision: 1532 $ - * @todo Do we seriously need this wrapper? - */ -class Statement -{ - /** - * @var Doctrine_Connection $conn Doctrine_Connection object, every connection - * statement holds an instance of Doctrine_Connection - */ - protected $_conn; - - /** - * @var Doctrine::DBAL::Driver::Statement - */ - protected $_stmt; - - /** - * constructor - * - * @param Doctrine_Connection $conn Doctrine_Connection object, every connection - * statement holds an instance of Doctrine_Connection - * @param mixed $stmt - */ - public function __construct(Connection $conn, $stmt) - { - $this->_conn = $conn; - $this->_stmt = $stmt; - - if ($stmt === false) { - throw \Doctrine\Common\DoctrineException::updateMe('Unknown statement object given.'); - } - } - - /** - * getConnection - * returns the connection object this statement uses - * - * @return Doctrine_Connection - */ - public function getConnection() - { - return $this->_conn; - } - public function getStatement() - { - return $this->_stmt; - } - public function getQuery() - { - return $this->_stmt->queryString; - } - - /** - * Bind a column to a PHP variable - * - * @param mixed $column Number of the column (1-indexed) or name of the column in the result set. - * If using the column name, be aware that the name should match - * the case of the column, as returned by the driver. - * - * @param string $param Name of the PHP variable to which the column will be bound. - * @param integer $type Data type of the parameter, specified by the PDO::PARAM_* constants. - * @return boolean Returns TRUE on success or FALSE on failure - */ - public function bindColumn($column, $param, $type = null) - { - if ($type === null) { - return $this->_stmt->bindColumn($column, $param); - } else { - return $this->_stmt->bindColumn($column, $param, $type); - } - } - - /** - * bindValue - * Binds a value to a corresponding named or question mark - * placeholder in the SQL statement that was use to prepare the statement. - * - * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, - * this will be a parameter name of the form :name. For a prepared statement - * using question mark placeholders, this will be the 1-indexed position of the parameter - * - * @param mixed $value The value to bind to the parameter. - * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. - * - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function bindValue($param, $value, $type = null) - { - if ($type === null) { - return $this->_stmt->bindValue($param, $value); - } else { - return $this->_stmt->bindValue($param, $value, $type); - } - } - - /** - * Binds a PHP variable to a corresponding named or question mark placeholder in the - * SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), - * the variable is bound as a reference and will only be evaluated at the time - * that PDOStatement->execute() is called. - * - * Most parameters are input parameters, that is, parameters that are - * used in a read-only fashion to build up the query. Some drivers support the invocation - * of stored procedures that return data as output parameters, and some also as input/output - * parameters that both send in data and are updated to receive it. - * - * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, - * this will be a parameter name of the form :name. For a prepared statement - * using question mark placeholders, this will be the 1-indexed position of the parameter - * - * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. - * - * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return - * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the - * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. - * - * @param integer $length Length of the data type. To indicate that a parameter is an OUT parameter - * from a stored procedure, you must explicitly set the length. - * @param mixed $driverOptions - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array()) - { - if ($type === null) { - return $this->_stmt->bindParam($column, $variable); - } else { - return $this->_stmt->bindParam($column, $variable, $type, $length, $driverOptions); - } - } - - /** - * Closes the cursor, enabling the statement to be executed again. - * - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function closeCursor() - { - return $this->_stmt->closeCursor(); - } - - /** - * Returns the number of columns in the result set - * - * @return integer Returns the number of columns in the result set represented - * by the PDOStatement object. If there is no result set, - * this method should return 0. - */ - public function columnCount() - { - return $this->_stmt->columnCount(); - } - - /** - * Fetch the SQLSTATE associated with the last operation on the statement handle - * - * @see Doctrine_Adapter_Interface::errorCode() - * @return string error code string - */ - public function errorCode() - { - return $this->_stmt->errorCode(); - } - - /** - * Fetch extended error information associated with the last operation on the statement handle - * - * @see Doctrine_Adapter_Interface::errorInfo() - * @return array error info array - */ - public function errorInfo() - { - return $this->_stmt->errorInfo(); - } - - /** - * Executes a prepared statement - * - * If the prepared statement included parameter markers, you must either: - * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: - * bound variables pass their value as input and receive the output value, - * if any, of their associated parameter markers or pass an array of input-only - * parameter values - * - * - * @param array $params An array of values with as many elements as there are - * bound parameters in the SQL statement being executed. - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function execute($params = null) - { - try { - //$event = new Doctrine_Event($this, Doctrine_Event::STMT_EXECUTE, $this->getQuery(), $params); - //$this->_conn->getListener()->preStmtExecute($event); - - $result = true; - //if ( ! $event->skipOperation) { - $result = $this->_stmt->execute($params); - //$this->_conn->incrementQueryCount(); - //} - - //$this->_conn->getListener()->postStmtExecute($event); - - return $result; - } catch (PDOException $e) { - $this->_conn->rethrowException($e, $this); - } - - return false; - } - - /** - * fetch - * - * @see Query::HYDRATE_* constants - * @param integer $fetchStyle Controls how the next row will be returned to the caller. - * This value must be one of the Query::HYDRATE_* constants, - * defaulting to Query::HYDRATE_BOTH - * - * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, - * this value determines which row will be returned to the caller. - * This value must be one of the Query::HYDRATE_ORI_* constants, defaulting to - * Query::HYDRATE_ORI_NEXT. To request a scrollable cursor for your - * PDOStatement object, - * you must set the PDO::ATTR_CURSOR attribute to Doctrine::CURSOR_SCROLL when you - * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). - * - * @param integer $cursorOffset For a PDOStatement object representing a scrollable cursor for which the - * $cursorOrientation parameter is set to Query::HYDRATE_ORI_ABS, this value specifies - * the absolute number of the row in the result set that shall be fetched. - * - * For a PDOStatement object representing a scrollable cursor for - * which the $cursorOrientation parameter is set to Query::HYDRATE_ORI_REL, this value - * specifies the row to fetch relative to the cursor position before - * PDOStatement->fetch() was called. - * - * @return mixed - */ - public function fetch($fetchMode = Query::HYDRATE_BOTH, - $cursorOrientation = Query::HYDRATE_ORI_NEXT, - $cursorOffset = null) - { - //$event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCH, $this->getQuery()); - //$event->fetchMode = $fetchMode; - //$event->cursorOrientation = $cursorOrientation; - //$event->cursorOffset = $cursorOffset; - - //$data = $this->_conn->getListener()->preFetch($event); - - //if ( ! $event->skipOperation) { - $data = $this->_stmt->fetch($fetchMode, $cursorOrientation, $cursorOffset); - //} - - //$this->_conn->getListener()->postFetch($event); - - return $data; - } - - /** - * Returns an array containing all of the result set rows - * - * @param integer $fetchMode Controls how the next row will be returned to the caller. - * This value must be one of the Query::HYDRATE_* constants, - * defaulting to Query::HYDRATE_BOTH - * - * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is - * Query::HYDRATE_COLUMN. Defaults to 0. - * - * @return array - */ - public function fetchAll($fetchMode = Query::HYDRATE_BOTH, $columnIndex = null) - { - //$event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCHALL, $this->getQuery()); - //$event->fetchMode = $fetchMode; - //$event->columnIndex = $columnIndex; - //$this->_conn->getListener()->preFetchAll($event); - - //if ( ! $event->skipOperation) { - if ($columnIndex !== null) { - $data = $this->_stmt->fetchAll($fetchMode, $columnIndex); - } else { - $data = $this->_stmt->fetchAll($fetchMode); - } - //$event->data = $data; - //} - - //$this->_conn->getListener()->postFetchAll($event); - - return $data; - } - - /** - * Returns a single column from the next row of a - * result set or FALSE if there are no more rows. - * - * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no - * value is supplied, PDOStatement->fetchColumn() - * fetches the first column. - * - * @return string returns a single column in the next row of a result set. - */ - public function fetchColumn($columnIndex = 0) - { - return $this->_stmt->fetchColumn($columnIndex); - } - - /** - * Fetches the next row and returns it as an object. - * - * Fetches the next row and returns it as an object. This function is an alternative to - * PDOStatement->fetch() with Query::HYDRATE_CLASS or Query::HYDRATE_OBJ style. - * - * @param string $className Name of the created class, defaults to stdClass. - * @param array $args Elements of this array are passed to the constructor. - * - * @return mixed an instance of the required class with property names that correspond - * to the column names or FALSE in case of an error. - */ - public function fetchObject($className = 'stdClass', $args = array()) - { - return $this->_stmt->fetchObject($className, $args); - } - - /** - * Retrieve a statement attribute - * - * @param integer $attribute - * @see Doctrine::ATTR_* constants - * @return mixed the attribute value - */ - public function getAttribute($attribute) - { - return $this->_stmt->getAttribute($attribute); - } - - /** - * Returns metadata for a column in a result set - * - * @param integer $column The 0-indexed column in the result set. - * - * @return array Associative meta data array with the following structure: - * - * native_type The PHP native type used to represent the column value. - * driver:decl_ type The SQL type used to represent the column value in the database. If the column in the result set is the result of a function, this value is not returned by PDOStatement->getColumnMeta(). - * flags Any flags set for this column. - * name The name of this column as returned by the database. - * len The length of this column. Normally -1 for types other than floating point decimals. - * precision The numeric precision of this column. Normally 0 for types other than floating point decimals. - * pdo_type The type of this column as represented by the PDO::PARAM_* constants. - */ - public function getColumnMeta($column) - { - return $this->_stmt->getColumnMeta($column); - } - - /** - * Advances to the next rowset in a multi-rowset statement handle - * - * Some database servers support stored procedures that return more than one rowset - * (also known as a result set). The nextRowset() method enables you to access the second - * and subsequent rowsets associated with a PDOStatement object. Each rowset can have a - * different set of columns from the preceding rowset. - * - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function nextRowset() - { - return $this->_stmt->nextRowset(); - } - - /** - * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement - * executed by the corresponding object. - * - * If the last SQL statement executed by the associated Statement object was a SELECT statement, - * some databases may return the number of rows returned by that statement. However, - * this behaviour is not guaranteed for all databases and should not be - * relied on for portable applications. - * - * @return integer Returns the number of rows. - */ - public function rowCount() - { - return $this->_stmt->rowCount(); - } - - /** - * Set a statement attribute - * - * @param integer $attribute - * @param mixed $value the value of given attribute - * @return boolean Returns TRUE on success or FALSE on failure. - */ - public function setAttribute($attribute, $value) - { - return $this->_stmt->setAttribute($attribute, $value); - } - - /** - * Set the default fetch mode for this statement - * - * @param integer $mode The fetch mode must be one of the Query::HYDRATE_* constants. - * @return boolean Returns 1 on success or FALSE on failure. - */ - public function setFetchMode($mode, $arg1 = null, $arg2 = null) - { - return $this->_stmt->setFetchMode($mode, $arg1, $arg2); - } -} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Types/BooleanType.php b/lib/Doctrine/DBAL/Types/BooleanType.php index 415cead0b..98b0a5b8d 100644 --- a/lib/Doctrine/DBAL/Types/BooleanType.php +++ b/lib/Doctrine/DBAL/Types/BooleanType.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Types; +use Doctrine\DBAL\Platforms\AbstractPlatform; + /** * Type that maps an SQL boolean to a PHP boolean. * @@ -9,17 +11,17 @@ namespace Doctrine\DBAL\Types; */ class BooleanType extends Type { - public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getBooleanDeclarationSql(); } - public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $platform->convertBooleans($value); } - public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform) { return (bool) $value; } diff --git a/lib/Doctrine/DBAL/Types/DateTimeType.php b/lib/Doctrine/DBAL/Types/DateTimeType.php index 44c3a7183..50d9a2c04 100644 --- a/lib/Doctrine/DBAL/Types/DateTimeType.php +++ b/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Types; +use Doctrine\DBAL\Platforms\AbstractPlatform; + /** * Type that maps an SQL DATETIME to a PHP DateTime object. * @@ -14,17 +16,17 @@ class DateTimeType extends Type return 'DateTime'; } - public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getDateTimeTypeDeclarationSql($fieldDeclaration); } - public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value->format($platform->getDateTimeFormatString()); } - public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform) { return \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); } diff --git a/lib/Doctrine/DBAL/Types/DateType.php b/lib/Doctrine/DBAL/Types/DateType.php index 7695e48c1..aa3b93301 100644 --- a/lib/Doctrine/DBAL/Types/DateType.php +++ b/lib/Doctrine/DBAL/Types/DateType.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Types; +use Doctrine\DBAL\Platforms\AbstractPlatform; + /** * Type that maps an SQL DATETIME to a PHP Date object. * @@ -14,17 +16,17 @@ class DateType extends Type return 'Date'; } - public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getDateTypeDeclarationSql($fieldDeclaration); } - public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value->format($platform->getDateFormatString()); } - public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform) { return \DateTime::createFromFormat($platform->getDateFormatString(), $value); } diff --git a/lib/Doctrine/DBAL/Types/TimeType.php b/lib/Doctrine/DBAL/Types/TimeType.php index ceba1975b..ddd13be41 100644 --- a/lib/Doctrine/DBAL/Types/TimeType.php +++ b/lib/Doctrine/DBAL/Types/TimeType.php @@ -2,6 +2,8 @@ namespace Doctrine\DBAL\Types; +use Doctrine\DBAL\Platforms\AbstractPlatform; + /** * Type that maps an SQL DATETIME to a PHP Date object. * @@ -17,7 +19,7 @@ class TimeType extends Type /** * {@inheritdoc} */ - public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getTimeTypeDeclarationSql($fieldDeclaration); } @@ -27,7 +29,7 @@ class TimeType extends Type * * @override */ - public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value->format($platform->getTimeFormatString()); } @@ -37,7 +39,7 @@ class TimeType extends Type * * @override */ - public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform) { return \DateTime::createFromFormat($platform->getTimeFormatString(), $value); } diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index 07dc46d30..16896da61 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -24,7 +24,10 @@ abstract class Type const CODE_STR = 2; const CODE_LOB = 3; + /** Map of already instantiated type objects. One instance per type (flyweight). */ private static $_typeObjects = array(); + + /** The map of supported doctrine mapping types. */ private static $_typesMap = array( 'array' => 'Doctrine\DBAL\Types\ArrayType', 'object' => 'Doctrine\DBAL\Types\ObjectType', @@ -45,25 +48,63 @@ abstract class Type /* Prevent instantiation and force use of the factory method. */ private function __construct() {} - public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value; } - public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) { return $value; } - public function getDefaultLength(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + /** + * Gets the default length of this type. + * + * @todo Needed? + */ + public function getDefaultLength(AbstractPlatform $platform) { return null; } + + /** + * Gets the SQL declaration snippet for a field of this type. + * + * @param array $fieldDeclaration The field declaration. + * @param AbstractPlatform $platform The currently used database platform. + */ + abstract public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform); - abstract public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform); - + /** + * Gets the name of this type. + * + * @return string + * @todo Needed? + */ abstract public function getName(); + /** + * Gets the type code of this type. + * + * @return integer + */ public function getTypeCode() { return self::CODE_STR; diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 72a8855ef..862db24f2 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -264,7 +264,7 @@ class EntityManager /** * @todo Implementation. */ - public function createCriteria() + public function createQueryBuilder() { //... } @@ -405,7 +405,7 @@ class EntityManager * overriding any local changes that have not yet been persisted. * * @param object $entity - * @todo Implemntation + * @todo Implementation */ public function refresh($entity) { @@ -559,7 +559,7 @@ class EntityManager } /** - * + * Gets the proxy generated used by the EntityManager to create entity proxies. */ public function getProxyGenerator() { @@ -591,8 +591,6 @@ class EntityManager $eventManager = new EventManager(); } - $em = new EntityManager($conn, $config, $eventManager); - - return $em; + return new EntityManager($conn, $config, $eventManager); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 6c0ce51b4..826feb336 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -1121,6 +1121,16 @@ final class ClassMetadata { return $this->primaryTable['name']; } + + /** + * Gets the table name to use for temporary identifier tables of this class. + * + * @return string + */ + public function getTemporaryIdTableName() + { + return $this->primaryTable['name'] . '_id_tmp'; + } public function getInheritedFields() { diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index f04aaec2b..781a199cb 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -143,12 +143,12 @@ class StandardEntityPersister $params = array(); foreach ($insertData[$primaryTableName] as $value) { $params[$paramIndex] = $value; - $stmt->bindValue($paramIndex++, $value/*, Type::getType()*/); + $stmt->bindValue($paramIndex++, $value); } $sqlLogger->logSql($this->_class->insertSql, $params); } else { foreach ($insertData[$primaryTableName] as $value) { - $stmt->bindValue($paramIndex++, $value/*, Type::getType()*/); + $stmt->bindValue($paramIndex++, $value); } } @@ -201,8 +201,8 @@ class StandardEntityPersister public function delete($entity) { $id = array_combine( - $this->_class->getIdentifierFieldNames(), - $this->_em->getUnitOfWork()->getEntityIdentifier($entity) + $this->_class->getIdentifierFieldNames(), + $this->_em->getUnitOfWork()->getEntityIdentifier($entity) ); $this->_conn->delete($this->_class->primaryTable['name'], $id); } @@ -238,11 +238,12 @@ class StandardEntityPersister } /** - * Gets the table name to use for temporary identifier tables. + * Gets the table name to use for temporary identifier tables of the class + * persisted by this persister. */ public function getTemporaryIdTableName() { - //... + return $this->_class->primaryTable['name'] . '_id_tmp'; } /** @@ -378,8 +379,7 @@ class StandardEntityPersister } else { // Inject collection $this->_class->reflFields[$field]->setValue( - $entity, new PersistentCollection($this->_em, - $this->_em->getClassMetadata($assoc->targetEntityName) + $entity, new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName) )); } } @@ -410,7 +410,7 @@ class StandardEntityPersister } return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName() - . ' WHERE ' . $conditionSql; + . ' WHERE ' . $conditionSql; } /** diff --git a/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php b/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php index 685bb4ffa..491a289cd 100644 --- a/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php @@ -68,17 +68,18 @@ abstract class AbstractExecutor implements \Serializable $isDeleteStatement = $AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement; $isUpdateStatement = $AST instanceof \Doctrine\ORM\Query\AST\UpdateStatement; - if ($isUpdateStatement || $isDeleteStatement) { - // TODO: Inspect the $AST and create the proper executor like so (pseudo-code): - /* - if (primaryClassInFromClause->isMultiTable()) { - if ($isDeleteStatement) { - return new Doctrine_ORM_Query_SqlExecutor_MultiTableDelete($AST); - } else { - return new Doctrine_ORM_Query_SqlExecutor_MultiTableUpdate($AST); - } - } else ... - */ + if ($isDeleteStatement) { + $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata( + $AST->getDeleteClause()->getAbstractSchemaName() + ); + + if ($primaryClass->isInheritanceTypeJoined()) { + return new MultiTableDeleteExecutor($AST, $sqlWalker); + } else { + return new SingleTableDeleteUpdateExecutor($AST, $sqlWalker); + } + } else if ($isUpdateStatement) { + //TODO: Check whether to pick MultiTableUpdateExecutor instead return new SingleTableDeleteUpdateExecutor($AST, $sqlWalker); } else { return new SingleSelectExecutor($AST, $sqlWalker); diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index d69a68f6a..fce31918e 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -30,37 +30,98 @@ namespace Doctrine\ORM\Query\Exec; * @link http://www.doctrine-project.org * @since 2.0 * @version $Revision$ - * @todo For a good implementation that uses temporary tables see the Hibernate sources: - * (org.hibernate.hql.ast.exec.MultiTableDeleteExecutor). */ class MultiTableDeleteExecutor extends AbstractExecutor { - public function __construct($AST) + private $_createTempTableSql; + private $_dropTempTableSql; + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * @param Node $AST The root AST node of the DQL query. + * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. + */ + public function __construct(\Doctrine\ORM\Query\AST\Node $AST, $sqlWalker) { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + + $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata( + $AST->getDeleteClause()->getAbstractSchemaName() + ); + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $tempTable = $rootClass->getTemporaryIdTableName(); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + // 1. Create a INSERT INTO temptable ... VALUES ( SELECT statement where the SELECT statement // selects the identifiers and uses the WhereClause of the $AST ). + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT ' . $idColumnList . ' FROM ' . $conn->quoteIdentifier($rootClass->primaryTable['name']) . ' t0'; + + // Append WHERE clause, if there is one. + if ($AST->getWhereClause()) { + $sqlWalker->setSqlTableAlias($rootClass->primaryTable['name'] . $AST->getDeleteClause()->getAliasIdentificationVariable(), 't0'); + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->getWhereClause()); + } // 2. Create ID subselect statement used in DELETE .... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; // 3. Create and store DELETE statements - /*$subselect = 'SELECT id1, id2 FROM temptable'; - foreach ($tableNames as $tableName) { - $this->_sqlStatements[] = 'DELETE FROM ' . $tableName . ' WHERE (id1, id2) IN (subselect)'; - }*/ - // in $this->_sqlStatements - } + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + foreach (array_reverse($classNames) as $className) { + $tableName = $em->getClassMetadata($className)->primaryTable['name']; + $this->_sqlStatements[] = 'DELETE FROM ' . $conn->quoteIdentifier($tableName) + . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => \Doctrine\DBAL\Types\Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + $this->_createTempTableSql = 'CREATE TEMPORARY TABLE ' . $tempTable . ' (' + . $conn->getDatabasePlatform()->getColumnDeclarationListSql($columnDefinitions) + . ', PRIMARY KEY(' . $idColumnList . '))'; + $this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; + } + /** * Executes all sql statements. * - * @param Doctrine_Connection $conn The database connection that is used to execute the queries. + * @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries. * @param array $params The parameters. * @override */ public function execute(\Doctrine\DBAL\Connection $conn, array $params) { - // 1. Create temporary id table if necessary + $numDeleted = 0; + + // Create temporary id table + $conn->exec($this->_createTempTableSql); + + // Insert identifiers + $conn->exec($this->_insertSql, $params); - //... - } + // Execute DELETE statements + for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) { + if ($i == $count-1) { + $numDeleted = $conn->exec($this->_sqlStatements[$i]); + } else { + $conn->exec($this->_sqlStatements[$i]); + } + } + + // Drop temporary table + $conn->exec($this->_dropTempTableSql); + + return $numDeleted; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 57d5df346..fe503f2db 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -85,12 +85,24 @@ class SqlWalker implements TreeWalker } /** + * Gets the Connection used by the walker. + * * @return Connection */ public function getConnection() { return $this->_em->getConnection(); } + + /** + * Gets the EntityManager used by the walker. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } /** * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. @@ -1203,7 +1215,25 @@ class SqlWalker implements TreeWalker } return $this->_dqlToSqlAliasMap[$tableName]; } + + /** + * Forces the SqlWalker to use a specific alias for a table name, rather than + * generating an alias on its own. + * + * @param string $tableName + * @param string $alias + */ + public function setSqlTableAlias($tableName, $alias) + { + $this->_dqlToSqlAliasMap[$tableName] = $alias; + } + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * @return string + */ public function getSqlColumnAlias($columnName) { return trim($columnName, '`') . $this->_aliasCounter++; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index c41f928a5..84cce2946 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -412,9 +412,7 @@ class UnitOfWork implements PropertyChangedListener $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $actualData[$name] ? $actualData[$name] : array()); $coll->setOwner($entity, $assoc); - if ( ! $coll->isEmpty()) { - $coll->setDirty(true); - } + $coll->setDirty( ! $coll->isEmpty()); $class->reflFields[$name]->setValue($entity, $coll); $actualData[$name] = $coll; } @@ -425,8 +423,7 @@ class UnitOfWork implements PropertyChangedListener // (only has an id). These result in an INSERT. $this->_originalEntityData[$oid] = $actualData; $this->_entityChangeSets[$oid] = array_map( - function($e) { return array(null, $e); }, - $actualData + function($e) { return array(null, $e); }, $actualData ); } else { // Entity is "fully" MANAGED: it was already fully persisted before @@ -519,7 +516,6 @@ class UnitOfWork implements PropertyChangedListener $data[$name] = $refProp->getValue($entry); $changeSet[$name] = array(null, $data[$name]); if (isset($targetClass->associationMappings[$name])) { - //echo "RECURSING INTO $name" . PHP_EOL; //TODO: Prevent infinite recursion $this->_computeAssociationChanges($targetClass->associationMappings[$name], $data[$name]); } @@ -1300,8 +1296,9 @@ class UnitOfWork implements PropertyChangedListener $id = array($data[$class->identifier[0]]); $idHash = $id[0]; } - $entity = $this->tryGetByIdHash($idHash, $class->rootEntityName); - if ($entity) { + + if (isset($this->_identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->_identityMap[$class->rootEntityName][$idHash]; $oid = spl_object_hash($entity); $overrideLocalChanges = false; //$overrideLocalChanges = $query->getHint('doctrine.refresh'); diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index 77aaf34a1..8d8c0cb87 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -65,6 +65,13 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(100000, $entities[0]->getSalary()); $this->_em->clear(); + + //TODO: Test bulk UPDATE + + $query = $this->_em->createQuery("delete from Doctrine\Tests\Models\Company\CompanyPerson p"); + + $numDeleted = $query->execute(); + $this->assertEquals(2, $numDeleted); } public function testMultiLevelUpdateAndFind() {