Commit a758b565 authored by romanb's avatar romanb

[2.0] Introduced SQL logging facilities. Made Type constructor private to...

[2.0] Introduced SQL logging facilities. Made Type constructor private to prevent instantiation and force use of the factory method getType().
parent 0ed8e7a3
...@@ -47,9 +47,30 @@ class Configuration ...@@ -47,9 +47,30 @@ class Configuration
public function __construct() public function __construct()
{ {
$this->_attributes = array( $this->_attributes = array(
'quoteIdentifiers' => false 'quoteIdentifiers' => false,
'sqlLogger' => null
); );
} }
/**
* Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled.
*
* @param SqlLogger $logger
*/
public function setSqlLogger($logger)
{
$this->_attributes['sqlLogger'] = $logger;
}
/**
* Gets the SQL logger that is used.
*
* @return SqlLogger
*/
public function getSqlLogger()
{
return $this->_attributes['sqlLogger'];
}
public function getQuoteIdentifiers() public function getQuoteIdentifiers()
{ {
......
...@@ -49,149 +49,149 @@ use Doctrine\Common\DoctrineException; ...@@ -49,149 +49,149 @@ use Doctrine\Common\DoctrineException;
* 'slaveConnectionResolver' => new MySlaveConnectionResolver(), * 'slaveConnectionResolver' => new MySlaveConnectionResolver(),
* 'masters' => array(...), * 'masters' => array(...),
* 'masterConnectionResolver' => new MyMasterConnectionResolver() * 'masterConnectionResolver' => new MyMasterConnectionResolver()
* *
* Doctrine\DBAL could ship with a simple standard broker that uses a primitive * Doctrine\DBAL could ship with a simple standard broker that uses a primitive
* round-robin approach to distribution. User can provide its own brokers. * round-robin approach to distribution. User can provide its own brokers.
*/ */
class Connection class Connection
{ {
/** /**
* Constant for transaction isolation level READ UNCOMMITTED. * Constant for transaction isolation level READ UNCOMMITTED.
*/ */
const TRANSACTION_READ_UNCOMMITTED = 1; const TRANSACTION_READ_UNCOMMITTED = 1;
/** /**
* Constant for transaction isolation level READ COMMITTED. * Constant for transaction isolation level READ COMMITTED.
*/ */
const TRANSACTION_READ_COMMITTED = 2; const TRANSACTION_READ_COMMITTED = 2;
/** /**
* Constant for transaction isolation level REPEATABLE READ. * Constant for transaction isolation level REPEATABLE READ.
*/ */
const TRANSACTION_REPEATABLE_READ = 3; const TRANSACTION_REPEATABLE_READ = 3;
/** /**
* Constant for transaction isolation level SERIALIZABLE. * Constant for transaction isolation level SERIALIZABLE.
*/ */
const TRANSACTION_SERIALIZABLE = 4; const TRANSACTION_SERIALIZABLE = 4;
/** /**
* The wrapped driver connection. * The wrapped driver connection.
* *
* @var Doctrine\DBAL\Driver\Connection * @var Doctrine\DBAL\Driver\Connection
*/ */
protected $_conn; protected $_conn;
/** /**
* The Configuration. * The Configuration.
* *
* @var Doctrine\DBAL\Configuration * @var Doctrine\DBAL\Configuration
*/ */
protected $_config; protected $_config;
/** /**
* The EventManager. * The EventManager.
* *
* @var Doctrine\Common\EventManager * @var Doctrine\Common\EventManager
*/ */
protected $_eventManager; protected $_eventManager;
/** /**
* Whether or not a connection has been established. * Whether or not a connection has been established.
* *
* @var boolean * @var boolean
*/ */
protected $_isConnected = false; protected $_isConnected = false;
/** /**
* The transaction nesting level. * The transaction nesting level.
* *
* @var integer * @var integer
*/ */
protected $_transactionNestingLevel = 0; protected $_transactionNestingLevel = 0;
/** /**
* The currently active transaction isolation level. * The currently active transaction isolation level.
* *
* @var integer * @var integer
*/ */
protected $_transactionIsolationLevel; protected $_transactionIsolationLevel;
/** /**
* The parameters used during creation of the Connection instance. * The parameters used during creation of the Connection instance.
* *
* @var array * @var array
*/ */
protected $_params = array(); protected $_params = array();
/** /**
* The query count. Represents the number of executed database queries by the connection. * The query count. Represents the number of executed database queries by the connection.
* *
* @var integer * @var integer
*/ */
protected $_queryCount = 0; protected $_queryCount = 0;
/** /**
* The DatabasePlatform object that provides information about the * The DatabasePlatform object that provides information about the
* database platform used by the connection. * database platform used by the connection.
* *
* @var Doctrine\DBAL\Platforms\AbstractPlatform * @var Doctrine\DBAL\Platforms\AbstractPlatform
*/ */
protected $_platform; protected $_platform;
/** /**
* The schema manager. * The schema manager.
* *
* @var Doctrine\DBAL\Schema\SchemaManager * @var Doctrine\DBAL\Schema\SchemaManager
*/ */
protected $_schemaManager; protected $_schemaManager;
/** /**
* The used DBAL driver. * The used DBAL driver.
* *
* @var Doctrine\DBAL\Driver * @var Doctrine\DBAL\Driver
*/ */
protected $_driver; protected $_driver;
/** /**
* Whether to quote identifiers. Read from the configuration upon construction. * Whether to quote identifiers. Read from the configuration upon construction.
* *
* @var boolean * @var boolean
*/ */
protected $_quoteIdentifiers = false; protected $_quoteIdentifiers = false;
/** /**
* Initializes a new instance of the Connection class. * Initializes a new instance of the Connection class.
* *
* @param array $params The connection parameters. * @param array $params The connection parameters.
* @param Driver $driver * @param Driver $driver
* @param Configuration $config * @param Configuration $config
* @param EventManager $eventManager * @param EventManager $eventManager
*/ */
public function __construct(array $params, Driver $driver, Configuration $config = null, public function __construct(array $params, Driver $driver, Configuration $config = null,
EventManager $eventManager = null) EventManager $eventManager = null)
{ {
$this->_driver = $driver; $this->_driver = $driver;
$this->_params = $params; $this->_params = $params;
if (isset($params['pdo'])) { if (isset($params['pdo'])) {
$this->_conn = $params['pdo']; $this->_conn = $params['pdo'];
$this->_isConnected = true; $this->_isConnected = true;
} }
// Create default config and event manager if none given // Create default config and event manager if none given
if ( ! $config) { if ( ! $config) {
$config = new Configuration(); $config = new Configuration();
} }
if ( ! $eventManager) { if ( ! $eventManager) {
$eventManager = new EventManager(); $eventManager = new EventManager();
} }
$this->_config = $config; $this->_config = $config;
$this->_eventManager = $eventManager; $this->_eventManager = $eventManager;
$this->_platform = $driver->getDatabasePlatform(); $this->_platform = $driver->getDatabasePlatform();
$this->_transactionIsolationLevel = $this->_platform->getDefaultTransactionIsolationLevel(); $this->_transactionIsolationLevel = $this->_platform->getDefaultTransactionIsolationLevel();
$this->_quoteIdentifiers = $config->getQuoteIdentifiers(); $this->_quoteIdentifiers = $config->getQuoteIdentifiers();
$this->_platform->setQuoteIdentifiers($this->_quoteIdentifiers); $this->_platform->setQuoteIdentifiers($this->_quoteIdentifiers);
} }
/** /**
* Get the array of parameters used to instantiated this connection instance * Get the array of parameters used to instantiated this connection instance
* *
...@@ -212,46 +212,46 @@ class Connection ...@@ -212,46 +212,46 @@ class Connection
return $this->_driver->getDatabase($this); return $this->_driver->getDatabase($this);
} }
/** /**
* Gets the DBAL driver instance. * Gets the DBAL driver instance.
* *
* @return Doctrine\DBAL\Driver * @return Doctrine\DBAL\Driver
*/ */
public function getDriver() public function getDriver()
{ {
return $this->_driver; return $this->_driver;
} }
/** /**
* Gets the Configuration used by the Connection. * Gets the Configuration used by the Connection.
* *
* @return Doctrine\DBAL\Configuration * @return Doctrine\DBAL\Configuration
*/ */
public function getConfiguration() public function getConfiguration()
{ {
return $this->_config; return $this->_config;
} }
/** /**
* Gets the EventManager used by the Connection. * Gets the EventManager used by the Connection.
* *
* @return Doctrine\Common\EventManager * @return Doctrine\Common\EventManager
*/ */
public function getEventManager() public function getEventManager()
{ {
return $this->_eventManager; return $this->_eventManager;
} }
/** /**
* Gets the DatabasePlatform for the connection. * Gets the DatabasePlatform for the connection.
* *
* @return Doctrine\DBAL\Platforms\AbstractPlatform * @return Doctrine\DBAL\Platforms\AbstractPlatform
*/ */
public function getDatabasePlatform() public function getDatabasePlatform()
{ {
return $this->_platform; return $this->_platform;
} }
/** /**
* Establishes the connection with the database. * Establishes the connection with the database.
* *
...@@ -260,190 +260,25 @@ class Connection ...@@ -260,190 +260,25 @@ class Connection
public function connect() public function connect()
{ {
if ($this->_isConnected) return false; if ($this->_isConnected) return false;
$driverOptions = isset($this->_params['driverOptions']) ? $driverOptions = isset($this->_params['driverOptions']) ?
$this->_params['driverOptions'] : array(); $this->_params['driverOptions'] : array();
$user = isset($this->_params['user']) ? $user = isset($this->_params['user']) ?
$this->_params['user'] : null; $this->_params['user'] : null;
$password = isset($this->_params['password']) ? $password = isset($this->_params['password']) ?
$this->_params['password'] : null; $this->_params['password'] : null;
$this->_conn = $this->_driver->connect( $this->_conn = $this->_driver->connect(
$this->_params, $this->_params,
$user, $user,
$password, $password,
$driverOptions $driverOptions
); );
$this->_isConnected = true; $this->_isConnected = true;
return true; return true;
} }
/**
* Whether an actual connection to the database is established.
*
* @return boolean
*/
public function isConnected()
{
return $this->_isConnected;
}
/**
* Deletes table row(s) matching the specified identifier.
*
* @param string $table The table to delete data from
* @param array $identifier An associateve array containing identifier fieldname-value pairs.
* @return integer The number of affected rows
*/
public function delete($tableName, array $identifier)
{
$this->connect();
$criteria = array();
foreach (array_keys($identifier) as $id) {
$criteria[] = $this->quoteIdentifier($id) . ' = ?';
}
$query = 'DELETE FROM '
. $this->quoteIdentifier($tableName)
. ' WHERE ' . implode(' AND ', $criteria);
return $this->exec($query, array_values($identifier));
}
/**
* Updates table row(s) with specified data
*
* @throws Doctrine\DBAL\ConnectionException if something went wrong at the database level
* @param string $table The table to insert data into
* @param array $values An associateve array containing column-value pairs.
* @return mixed boolean false if empty value array was given,
* otherwise returns the number of affected rows
*/
public function update($tableName, array $data, array $identifier)
{
$this->connect();
if (empty($data)) {
return false;
}
$set = array();
foreach ($data as $columnName => $value) {
$set[] = $this->quoteIdentifier($columnName) . ' = ?';
}
$params = array_merge(array_values($data), array_values($identifier));
$sql = 'UPDATE ' . $this->quoteIdentifier($tableName)
. ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', array_keys($identifier))
. ' = ?';
return $this->exec($sql, $params);
}
/**
* Inserts a table row with specified data.
*
* @param string $table The table to insert data into.
* @param array $fields An associateve array containing fieldname-value pairs.
* @return mixed boolean false if empty value array was given,
* otherwise returns the number of affected rows
*/
public function insert($tableName, array $data)
{
$this->connect();
if (empty($data)) {
return false;
}
// column names are specified as array keys
$cols = array();
$a = array();
foreach ($data as $columnName => $value) {
$cols[] = $this->quoteIdentifier($columnName);
$a[] = '?';
}
$query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
. ' (' . implode(', ', $cols) . ') '
. 'VALUES (';
$query .= implode(', ', $a) . ')';
return $this->exec($query, array_values($data));
}
/**
* Set the charset on the current connection
*
* @param string charset
*/
public function setCharset($charset)
{
$this->exec($this->_platform->getSetCharsetSql($charset));
}
/**
* Quote a string so it can be safely used as a table or column name, even if
* it is a reserved name.
*
* Delimiting style depends on the underlying database platform that is being used.
*
* NOTE: Just because you CAN use delimited identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* @param string $str identifier name to be quoted
* @param bool $checkOption check the 'quote_identifier' option
*
* @return string quoted identifier string
*/
public function quoteIdentifier($str)
{
if ($this->_quoteIdentifiers) {
return $this->_platform->quoteIdentifier($str);
}
return $str;
}
/**
* Quotes a given input parameter.
*
* @param mixed $input Parameter to be quoted.
* @param string $type Type of the parameter.
* @return string The quoted parameter.
*/
public function quote($input, $type = null)
{
$this->connect();
return $this->_conn->quote($input, $type);
}
/**
* Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_ASSOC).
*
* @param string $sql The SQL query.
* @param array $params The query parameters.
* @return array
*/
public function fetchAll($sql, array $params = array())
{
return $this->execute($sql, $params)->fetchAll(\PDO::FETCH_ASSOC);
}
/**
* Convenience method for PDO::query("...") followed by $stmt->fetchColumn().
*
* @param string $statement The SQL query.
* @param array $params The query parameters.
* @param int $colnum 0-indexed column number to retrieve
* @return mixed
*/
public function fetchOne($statement, array $params = array(), $colnum = 0)
{
return $this->execute($statement, $params)->fetchColumn($colnum);
}
/** /**
* Convenience method for PDO::query("...") followed by $stmt->fetch(PDO::FETCH_ASSOC). * Convenience method for PDO::query("...") followed by $stmt->fetch(PDO::FETCH_ASSOC).
...@@ -468,7 +303,7 @@ class Connection ...@@ -468,7 +303,7 @@ class Connection
{ {
return $this->execute($statement, $params)->fetch(\PDO::FETCH_NUM); return $this->execute($statement, $params)->fetch(\PDO::FETCH_NUM);
} }
/** /**
* Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_COLUMN, ...). * Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_COLUMN, ...).
* *
...@@ -482,6 +317,16 @@ class Connection ...@@ -482,6 +317,16 @@ class Connection
return $this->execute($statement, $params)->fetchAll(\PDO::FETCH_COLUMN, $colnum); return $this->execute($statement, $params)->fetchAll(\PDO::FETCH_COLUMN, $colnum);
} }
/**
* Whether an actual connection to the database is established.
*
* @return boolean
*/
public function isConnected()
{
return $this->_isConnected;
}
/** /**
* Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_BOTH). * Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_BOTH).
* *
...@@ -493,108 +338,27 @@ class Connection ...@@ -493,108 +338,27 @@ class Connection
{ {
return $this->execute($statement, $params)->fetchAll(\PDO::FETCH_BOTH); return $this->execute($statement, $params)->fetchAll(\PDO::FETCH_BOTH);
} }
/**
* Prepares an SQL statement.
*
* @param string $statement
* @return Statement
*/
public function prepare($statement)
{
$this->connect();
return $this->_conn->prepare($statement);
}
/**
* Queries the database with limit and offset added to the query and returns
* a Statement object.
*
* @param string $query
* @param integer $limit
* @param integer $offset
* @return Statement
*/
public function select($query, $limit = 0, $offset = 0)
{
if ($limit > 0 || $offset > 0) {
$query = $this->_platform->modifyLimitQuery($query, $limit, $offset);
}
return $this->execute($query);
}
/** /**
* Executes an SQL SELECT query with the given parameters. * Deletes table row(s) matching the specified identifier.
*
* @param string $query sql query
* @param array $params query parameters
* *
* @return PDOStatement * @param string $table The table to delete data from
* @param array $identifier An associateve array containing identifier fieldname-value pairs.
* @return integer The number of affected rows
*/ */
public function execute($query, array $params = array()) public function delete($tableName, array $identifier)
{ {
$this->connect(); $this->connect();
try { $criteria = array();
if ( ! empty($params)) { foreach (array_keys($identifier) as $id) {
$stmt = $this->prepare($query); $criteria[] = $this->quoteIdentifier($id) . ' = ?';
$stmt->execute($params);
return $stmt;
} else {
$stmt = $this->_conn->query($query);
$this->_queryCount++;
return $stmt;
}
} catch (PDOException $e) {
$this->rethrowException($e, $this);
} }
}
/** $query = 'DELETE FROM '
* Executes an SQL INSERT/UPDATE/DELETE query with the given parameters. . $this->quoteIdentifier($tableName)
* . ' WHERE ' . implode(' AND ', $criteria);
* @param string $query sql query
* @param array $params query parameters
*
* @return PDOStatement
* @todo Rename to executeUpdate().
*/
public function exec($query, array $params = array()) {
$this->connect();
try {
if ( ! empty($params)) {
var_dump($params);
$stmt = $this->prepare($query);
$stmt->execute($params);
return $stmt->rowCount();
} else {
$count = $this->_conn->exec($query);
$this->_queryCount++;
return $count;
}
} catch (PDOException $e) {
//TODO: Wrap
throw $e;
}
}
/** return $this->exec($query, array_values($identifier));
* Wraps the given exception into a driver-specific exception and rethrows it.
*
* @throws Doctrine\DBAL\ConnectionException
*/
public function rethrowException(\Exception $e, $invoker)
{
throw $e;
}
/**
* Returns the number of queries executed by the connection.
*
* @return integer
*/
public function getQueryCount()
{
return $this->_queryCount;
} }
/** /**
...@@ -629,165 +393,391 @@ class Connection ...@@ -629,165 +393,391 @@ class Connection
return $this->_transactionIsolationLevel; return $this->_transactionIsolationLevel;
} }
/** /**
* Returns the current transaction nesting level. * Updates table row(s) with specified data
* *
* @return integer The nesting level. A value of 0 means theres no active transaction. * @throws Doctrine\DBAL\ConnectionException if something went wrong at the database level
*/ * @param string $table The table to insert data into
public function getTransactionNestingLevel() * @param array $values An associateve array containing column-value pairs.
{ * @return mixed boolean false if empty value array was given,
return $this->_transactionNestingLevel; * otherwise returns the number of affected rows
} */
public function update($tableName, array $data, array $identifier)
/** {
* Fetch the SQLSTATE associated with the last operation on the database handle $this->connect();
* if (empty($data)) {
* @return integer return false;
*/ }
public function errorCode()
{ $set = array();
$this->connect(); foreach ($data as $columnName => $value) {
return $this->_conn->errorCode(); $set[] = $this->quoteIdentifier($columnName) . ' = ?';
} }
/** $params = array_merge(array_values($data), array_values($identifier));
* Fetch extended error information associated with the last operation on the database handle
* $sql = 'UPDATE ' . $this->quoteIdentifier($tableName)
* @return array . ' SET ' . implode(', ', $set)
*/ . ' WHERE ' . implode(' = ? AND ', array_keys($identifier))
public function errorInfo() . ' = ?';
{
$this->connect(); return $this->exec($sql, $params);
return $this->_conn->errorInfo(); }
}
/**
/** * Inserts a table row with specified data.
* Returns the ID of the last inserted row, or the last value from a sequence object, *
* depending on the underlying driver. * @param string $table The table to insert data into.
* * @param array $fields An associateve array containing fieldname-value pairs.
* Note: This method may not return a meaningful or consistent result across different drivers, * @return mixed boolean false if empty value array was given,
* because the underlying database may not even support the notion of auto-increment fields or sequences. * otherwise returns the number of affected rows
* */
* @param string $table Name of the table into which a new row was inserted. public function insert($tableName, array $data)
* @param string $field Name of the field into which a new row was inserted. {
*/ $this->connect();
public function lastInsertId($seqName = null) if (empty($data)) {
{ return false;
$this->connect(); }
return $this->_conn->lastInsertId($seqName);
} // column names are specified as array keys
$cols = array();
/** $a = array();
* Start a transaction or set a savepoint. foreach ($data as $columnName => $value) {
* $cols[] = $this->quoteIdentifier($columnName);
* if trying to set a savepoint and there is no active transaction $a[] = '?';
* a new transaction is being started. }
*
* @return boolean $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
*/ . ' (' . implode(', ', $cols) . ') '
public function beginTransaction() . 'VALUES (';
{ $query .= implode(', ', $a) . ')';
$this->connect();
if ($this->_transactionNestingLevel == 0) { return $this->exec($query, array_values($data));
$this->_conn->beginTransaction(); }
/**
* Set the charset on the current connection
*
* @param string charset
*/
public function setCharset($charset)
{
$this->exec($this->_platform->getSetCharsetSql($charset));
}
/**
* Quote a string so it can be safely used as a table or column name, even if
* it is a reserved name.
*
* Delimiting style depends on the underlying database platform that is being used.
*
* NOTE: Just because you CAN use delimited identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* @param string $str identifier name to be quoted
* @param bool $checkOption check the 'quote_identifier' option
*
* @return string quoted identifier string
*/
public function quoteIdentifier($str)
{
if ($this->_quoteIdentifiers) {
return $this->_platform->quoteIdentifier($str);
}
return $str;
}
/**
* Quotes a given input parameter.
*
* @param mixed $input Parameter to be quoted.
* @param string $type Type of the parameter.
* @return string The quoted parameter.
*/
public function quote($input, $type = null)
{
$this->connect();
return $this->_conn->quote($input, $type);
}
/**
* Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_ASSOC).
*
* @param string $sql The SQL query.
* @param array $params The query parameters.
* @return array
*/
public function fetchAll($sql, array $params = array())
{
return $this->execute($sql, $params)->fetchAll(\PDO::FETCH_ASSOC);
}
/**
* Convenience method for PDO::query("...") followed by $stmt->fetchColumn().
*
* @param string $statement The SQL query.
* @param array $params The query parameters.
* @param int $colnum 0-indexed column number to retrieve
* @return mixed
*/
public function fetchOne($statement, array $params = array(), $colnum = 0)
{
return $this->execute($statement, $params)->fetchColumn($colnum);
}
/**
* Prepares an SQL statement.
*
* @param string $statement
* @return Statement
*/
public function prepare($statement)
{
$this->connect();
return $this->_conn->prepare($statement);
}
/**
* Queries the database with limit and offset added to the query and returns
* a Statement object.
*
* @param string $query
* @param integer $limit
* @param integer $offset
* @return Statement
*/
public function select($query, $limit = 0, $offset = 0)
{
if ($limit > 0 || $offset > 0) {
$query = $this->_platform->modifyLimitQuery($query, $limit, $offset);
}
return $this->execute($query);
}
/**
* Executes an SQL SELECT query with the given parameters.
*
* @param string $query sql query
* @param array $params query parameters
*
* @return PDOStatement
*/
public function execute($query, array $params = array())
{
$this->connect();
if ($this->_config->getSqlLogger()) {
$this->_config->getSqlLogger()->logSql($query, $params);
} }
++$this->_transactionNestingLevel;
return true; if ( ! empty($params)) {
} $stmt = $this->prepare($query);
$stmt->execute($params);
/** return $stmt;
* Commits the database changes done during a transaction that is in } else {
* progress or release a savepoint. This function may only be called when $stmt = $this->_conn->query($query);
* auto-committing is disabled, otherwise it will fail. $this->_queryCount++;
* return $stmt;
* @return boolean FALSE if commit couldn't be performed, TRUE otherwise }
*/ }
public function commit()
{ /**
if ($this->_transactionNestingLevel == 0) { * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters.
throw ConnectionException::commitFailedNoActiveTransaction(); *
} * @param string $query sql query
* @param array $params query parameters
$this->connect(); *
* @return PDOStatement
if ($this->_transactionNestingLevel == 1) { * @todo Rename to executeUpdate().
$this->_conn->commit(); */
} public function exec($query, array $params = array()) {
--$this->_transactionNestingLevel; $this->connect();
return true; if ($this->_config->getSqlLogger()) {
} $this->_config->getSqlLogger()->logSql($query, $params);
}
/**
* Cancel any database changes done during a transaction or since a specific if ( ! empty($params)) {
* savepoint that is in progress. This function may only be called when $stmt = $this->prepare($query);
* auto-committing is disabled, otherwise it will fail. Therefore, a new $stmt->execute($params);
* transaction is implicitly started after canceling the pending changes. return $stmt->rowCount();
* } else {
* this method can be listened with onPreTransactionRollback and onTransactionRollback $count = $this->_conn->exec($query);
* eventlistener methods $this->_queryCount++;
* return $count;
* @param string $savepoint Name of a savepoint to rollback to. }
* @throws Doctrine\DBAL\ConnectionException If the rollback operation fails at database level. }
* @return boolean FALSE if rollback couldn't be performed, TRUE otherwise.
*/ /**
public function rollback() * Returns the number of queries executed by the connection.
{ *
if ($this->_transactionNestingLevel == 0) { * @return integer
throw ConnectionException::rollbackFailedNoActiveTransaction(); */
} public function getQueryCount()
{
$this->connect(); return $this->_queryCount;
}
if ($this->_transactionNestingLevel == 1) {
$this->_transactionNestingLevel = 0; /**
$this->_conn->rollback(); * Returns the current transaction nesting level.
*
} * @return integer The nesting level. A value of 0 means theres no active transaction.
--$this->_transactionNestingLevel; */
public function getTransactionNestingLevel()
return true; {
} return $this->_transactionNestingLevel;
}
/**
* Quotes pattern (% and _) characters in a string) /**
* * Fetch the SQLSTATE associated with the last operation on the database handle
* EXPERIMENTAL *
* * @return integer
* WARNING: this function is experimental and may change signature at */
* any time until labelled as non-experimental public function errorCode()
* {
* @param string the input string to quote $this->connect();
* return $this->_conn->errorCode();
* @return string quoted string }
*/
protected function _escapePattern($text) /**
{ * Fetch extended error information associated with the last operation on the database handle
return $text; *
} * @return array
*/
/** public function errorInfo()
* Gets the wrapped driver connection. {
* $this->connect();
* @return Doctrine\DBAL\Driver\Connection return $this->_conn->errorInfo();
*/ }
public function getWrappedConnection()
{ /**
$this->connect(); * Returns the ID of the last inserted row, or the last value from a sequence object,
return $this->_conn; * depending on the underlying driver.
} *
* Note: This method may not return a meaningful or consistent result across different drivers,
/** * because the underlying database may not even support the notion of auto-increment fields or sequences.
* Gets the SchemaManager that can be used to inspect or change the *
* database schema through the connection. * @param string $table Name of the table into which a new row was inserted.
* * @param string $field Name of the field into which a new row was inserted.
* @return Doctrine\DBAL\Schema\SchemaManager */
*/ public function lastInsertId($seqName = null)
public function getSchemaManager() {
{ $this->connect();
if ( ! $this->_schemaManager) { return $this->_conn->lastInsertId($seqName);
$this->_schemaManager = $this->_driver->getSchemaManager($this); }
}
return $this->_schemaManager; /**
} * Start a transaction or set a savepoint.
*
* if trying to set a savepoint and there is no active transaction
* a new transaction is being started.
*
* @return boolean
*/
public function beginTransaction()
{
$this->connect();
if ($this->_transactionNestingLevel == 0) {
$this->_conn->beginTransaction();
}
++$this->_transactionNestingLevel;
return true;
}
/**
* Commits the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail.
*
* @return boolean FALSE if commit couldn't be performed, TRUE otherwise
*/
public function commit()
{
if ($this->_transactionNestingLevel == 0) {
throw ConnectionException::commitFailedNoActiveTransaction();
}
$this->connect();
if ($this->_transactionNestingLevel == 1) {
$this->_conn->commit();
}
--$this->_transactionNestingLevel;
return true;
}
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* this method can be listened with onPreTransactionRollback and onTransactionRollback
* eventlistener methods
*
* @param string $savepoint Name of a savepoint to rollback to.
* @throws Doctrine\DBAL\ConnectionException If the rollback operation fails at database level.
* @return boolean FALSE if rollback couldn't be performed, TRUE otherwise.
*/
public function rollback()
{
if ($this->_transactionNestingLevel == 0) {
throw ConnectionException::rollbackFailedNoActiveTransaction();
}
$this->connect();
if ($this->_transactionNestingLevel == 1) {
$this->_transactionNestingLevel = 0;
$this->_conn->rollback();
}
--$this->_transactionNestingLevel;
return true;
}
/**
* Quotes pattern (% and _) characters in a string)
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change signature at
* any time until labelled as non-experimental
*
* @param string the input string to quote
*
* @return string quoted string
*/
protected function _escapePattern($text)
{
return $text;
}
/**
* Gets the wrapped driver connection.
*
* @return Doctrine\DBAL\Driver\Connection
*/
public function getWrappedConnection()
{
$this->connect();
return $this->_conn;
}
/**
* Gets the SchemaManager that can be used to inspect or change the
* database schema through the connection.
*
* @return Doctrine\DBAL\Schema\SchemaManager
*/
public function getSchemaManager()
{
if ( ! $this->_schemaManager) {
$this->_schemaManager = $this->_driver->getSchemaManager($this);
}
return $this->_schemaManager;
}
} }
\ No newline at end of file
<?php
namespace Doctrine\DBAL\Logging;
/**
* A SQL logger that logs to the standard output using echo/var_dump.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class EchoSqlLogger implements SqlLogger
{
public function logSql($sql, array $params = null)
{
echo $sql . PHP_EOL;
if ($params) {
var_dump($params);
}
}
}
\ No newline at end of file
<?php
namespace Doctrine\DBAL\Logging;
/**
* Interface for SQL loggers.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
interface SqlLogger
{
public function logSql($sql, array $params = null);
}
\ No newline at end of file
...@@ -37,6 +37,9 @@ abstract class Type ...@@ -37,6 +37,9 @@ abstract class Type
'double' => 'Doctrine\DBAL\Types\DoubleType' 'double' => 'Doctrine\DBAL\Types\DoubleType'
); );
/* Prevent instantiation and force use of the factory method. */
private function __construct() {}
public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform)
{ {
return $value; return $value;
......
...@@ -170,11 +170,11 @@ class EntityManager ...@@ -170,11 +170,11 @@ class EntityManager
} }
/** /**
* Starts a transaction on the underlying connection. * Starts a transaction on the underlying database connection.
*/ */
public function beginTransaction() public function beginTransaction()
{ {
return $this->_conn->beginTransaction(); $this->_conn->beginTransaction();
} }
/** /**
...@@ -182,15 +182,23 @@ class EntityManager ...@@ -182,15 +182,23 @@ class EntityManager
* *
* This causes a flush() of the EntityManager if the flush mode is set to * This causes a flush() of the EntityManager if the flush mode is set to
* AUTO or COMMIT. * AUTO or COMMIT.
*
* @return boolean
*/ */
public function commit() public function commit()
{ {
if ($this->_flushMode == self::FLUSHMODE_AUTO || $this->_flushMode == self::FLUSHMODE_COMMIT) { if ($this->_flushMode == self::FLUSHMODE_AUTO || $this->_flushMode == self::FLUSHMODE_COMMIT) {
$this->flush(); $this->flush();
} }
return $this->_conn->commitTransaction(); $this->_conn->commitTransaction();
}
/**
* Performs a rollback on the underlying database connection and closes the
* EntityManager as it may now be in a corrupted state.
*/
public function rollback()
{
$this->_conn->rollback();
$this->close();
} }
/** /**
...@@ -401,6 +409,7 @@ class EntityManager ...@@ -401,6 +409,7 @@ class EntityManager
*/ */
public function refresh($entity) public function refresh($entity)
{ {
$this->_errorIfClosed();
throw DoctrineException::notImplemented(); throw DoctrineException::notImplemented();
} }
......
...@@ -26,7 +26,7 @@ namespace Doctrine\ORM; ...@@ -26,7 +26,7 @@ namespace Doctrine\ORM;
* *
* This class cannot be instantiated. * This class cannot be instantiated.
* *
* @author robo * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
final class Events final class Events
...@@ -35,6 +35,9 @@ final class Events ...@@ -35,6 +35,9 @@ final class Events
const preDelete = 'preDelete'; const preDelete = 'preDelete';
const postDelete = 'postDelete'; const postDelete = 'postDelete';
const preSave = 'preSave'; const preInsert = 'preSave';
const postSave = 'postSave'; const postInsert = 'postSave';
const preUpdate = 'preUpdate';
const postUpdate = 'postUpdate';
const load = 'load';
} }
\ No newline at end of file
...@@ -185,7 +185,7 @@ abstract class AbstractHydrator ...@@ -185,7 +185,7 @@ abstract class AbstractHydrator
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName); $classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
$cache[$key]['fieldName'] = $fieldName; $cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false; $cache[$key]['isScalar'] = false;
$cache[$key]['type'] = Type::getType($classMetadata->getTypeOfField($fieldName)); $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else { } else {
......
...@@ -55,17 +55,12 @@ class ArrayHydrator extends AbstractHydrator ...@@ -55,17 +55,12 @@ class ArrayHydrator extends AbstractHydrator
/** @override */ /** @override */
protected function _hydrateAll() protected function _hydrateAll()
{ {
$s = microtime(true);
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result); $this->_hydrateRow($data, $cache, $result);
} }
$e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
return $result; return $result;
} }
......
...@@ -113,8 +113,6 @@ class ObjectHydrator extends AbstractHydrator ...@@ -113,8 +113,6 @@ class ObjectHydrator extends AbstractHydrator
*/ */
protected function _hydrateAll() protected function _hydrateAll()
{ {
$s = microtime(true);
$result = $this->_rsm->isMixed ? array() : new Collection; $result = $this->_rsm->isMixed ? array() : new Collection;
$cache = array(); $cache = array();
...@@ -132,10 +130,6 @@ class ObjectHydrator extends AbstractHydrator ...@@ -132,10 +130,6 @@ class ObjectHydrator extends AbstractHydrator
$this->_collections = array(); $this->_collections = array();
$this->_initializedRelations = array(); $this->_initializedRelations = array();
$e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records' . PHP_EOL;
return $result; return $result;
} }
...@@ -279,10 +273,10 @@ class ObjectHydrator extends AbstractHydrator ...@@ -279,10 +273,10 @@ class ObjectHydrator extends AbstractHydrator
*/ */
private function setRelatedElement($entity1, $property, $entity2) private function setRelatedElement($entity1, $property, $entity2)
{ {
$classMetadata1 = $this->_ce[get_class($entity1)]; $class = $this->_ce[get_class($entity1)];
$classMetadata1->reflFields[$property]->setValue($entity1, $entity2); $class->reflFields[$property]->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty(spl_object_hash($entity1), $property, $entity2); $this->_uow->setOriginalEntityProperty(spl_object_hash($entity1), $property, $entity2);
$relation = $classMetadata1->associationMappings[$property]; $relation = $class->associationMappings[$property];
if ($relation->isOneToOne()) { if ($relation->isOneToOne()) {
$targetClass = $this->_ce[$relation->targetEntityName]; $targetClass = $this->_ce[$relation->targetEntityName];
if ($relation->isOwningSide) { if ($relation->isOwningSide) {
...@@ -290,7 +284,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -290,7 +284,7 @@ class ObjectHydrator extends AbstractHydrator
if (isset($targetClass->inverseMappings[$property])) { if (isset($targetClass->inverseMappings[$property])) {
$sourceProp = $targetClass->inverseMappings[$property]->sourceFieldName; $sourceProp = $targetClass->inverseMappings[$property]->sourceFieldName;
$targetClass->reflFields[$sourceProp]->setValue($entity2, $entity1); $targetClass->reflFields[$sourceProp]->setValue($entity2, $entity1);
} else if ($classMetadata1 === $targetClass) { } else if ($class === $targetClass) {
// Special case: self-referencing one-one on the same class // Special case: self-referencing one-one on the same class
$targetClass->reflFields[$property]->setValue($entity2, $entity1); $targetClass->reflFields[$property]->setValue($entity2, $entity1);
} }
......
...@@ -253,15 +253,6 @@ final class ClassMetadata ...@@ -253,15 +253,6 @@ final class ClassMetadata
* @var array * @var array
*/ */
public $columnNames = array(); public $columnNames = array();
/**
* Map that maps lowercased column names (keys) to field names (values).
* Mainly used during hydration because Doctrine enforces PDO_CASE_LOWER
* for portability.
*
* @var array
*/
public $lcColumnToFieldNames = array();
/** /**
* Whether to automatically OUTER JOIN subtypes when a basetype is queried. * Whether to automatically OUTER JOIN subtypes when a basetype is queried.
...@@ -724,29 +715,6 @@ final class ClassMetadata ...@@ -724,29 +715,6 @@ final class ClassMetadata
$this->fieldNames[$columnName] : $columnName; $this->fieldNames[$columnName] : $columnName;
} }
/**
* Gets the field name for a completely lowercased column name.
* Mainly used during hydration.
*
* @param string $lcColumnName The all-lowercase column name.
* @return string The field name.
*/
public function getFieldNameForLowerColumnName($lcColumnName)
{
return $this->lcColumnToFieldNames[$lcColumnName];
}
/**
* Checks whether a specified column name (all lowercase) exists in this class.
*
* @param string $lcColumnName
* @return boolean
*/
public function hasLowerColumn($lcColumnName)
{
return isset($this->lcColumnToFieldNames[$lcColumnName]);
}
/** /**
* Validates & completes the given field mapping. * Validates & completes the given field mapping.
* *
...@@ -767,11 +735,9 @@ final class ClassMetadata ...@@ -767,11 +735,9 @@ final class ClassMetadata
if ( ! isset($mapping['columnName'])) { if ( ! isset($mapping['columnName'])) {
$mapping['columnName'] = $mapping['fieldName']; $mapping['columnName'] = $mapping['fieldName'];
} }
$lcColumnName = strtolower($mapping['columnName']);
$this->columnNames[$mapping['fieldName']] = $mapping['columnName']; $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
$this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
$this->lcColumnToFieldNames[$lcColumnName] = $mapping['fieldName'];
// Complete id mapping // Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) { if (isset($mapping['id']) && $mapping['id'] === true) {
......
...@@ -246,6 +246,7 @@ class ClassMetadataFactory ...@@ -246,6 +246,7 @@ class ClassMetadataFactory
// Generate INSERT SQL // Generate INSERT SQL
$columns = $values = array(); $columns = $values = array();
if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) { if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) {
// Generate INSERT SQL for inheritance type JOINED
foreach ($class->reflFields as $name => $field) { foreach ($class->reflFields as $name => $field) {
if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id']) if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id'])
|| isset($class->inheritedAssociationFields[$name])) { || isset($class->inheritedAssociationFields[$name])) {
...@@ -266,6 +267,7 @@ class ClassMetadataFactory ...@@ -266,6 +267,7 @@ class ClassMetadataFactory
} }
} }
} else { } else {
// Generate INSERT SQL for inheritance types NONE, SINGLE_TABLE, TABLE_PER_CLASS
foreach ($class->reflFields as $name => $field) { foreach ($class->reflFields as $name => $field) {
if (isset($class->associationMappings[$name])) { if (isset($class->associationMappings[$name])) {
$assoc = $class->associationMappings[$name]; $assoc = $class->associationMappings[$name];
...@@ -281,6 +283,8 @@ class ClassMetadataFactory ...@@ -281,6 +283,8 @@ class ClassMetadataFactory
} }
} }
} }
// Add discriminator column to the INSERT SQL if necessary
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() && $class->name == $class->rootEntityName) { if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() && $class->name == $class->rootEntityName) {
$columns[] = $class->discriminatorColumn['name']; $columns[] = $class->discriminatorColumn['name'];
$values[] = '?'; $values[] = '?';
......
...@@ -53,6 +53,7 @@ final class DoctrineColumn extends \Addendum\Annotation { ...@@ -53,6 +53,7 @@ final class DoctrineColumn extends \Addendum\Annotation {
public $length; public $length;
public $unique = false; public $unique = false;
public $nullable = false; public $nullable = false;
public $quote = false;
} }
final class DoctrineOneToOne extends \Addendum\Annotation { final class DoctrineOneToOne extends \Addendum\Annotation {
public $targetEntity; public $targetEntity;
......
<?php <?php
/* /*
* $Id$ * $Id$
* *
...@@ -36,227 +36,252 @@ use Doctrine\Common\DoctrineException; ...@@ -36,227 +36,252 @@ use Doctrine\Common\DoctrineException;
*/ */
class JoinedSubclassPersister extends StandardEntityPersister class JoinedSubclassPersister extends StandardEntityPersister
{ {
/** Map that maps column names to the table names that own them. /** Map that maps column names to the table names that own them.
* This is mainly a temporary cache, used during a single request. * This is mainly a temporary cache, used during a single request.
*/ */
private $_owningTableMap = array(); private $_owningTableMap = array();
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
*/ */
protected function _prepareData($entity, array &$result, $isInsert = false) protected function _prepareData($entity, array &$result, $isInsert = false)
{ {
parent::_prepareData($entity, $result, $isInsert); parent::_prepareData($entity, $result, $isInsert);
// Populate the discriminator column // Populate the discriminator column
if ($isInsert) { if ($isInsert) {
$discColumn = $this->_class->discriminatorColumn; $discColumn = $this->_class->discriminatorColumn;
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName); $rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$result[$rootClass->primaryTable['name']][$discColumn['name']] = $result[$rootClass->primaryTable['name']][$discColumn['name']] =
$this->_class->discriminatorValue; $this->_class->discriminatorValue;
} }
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
*/ */
public function getOwningTable($fieldName) public function getOwningTable($fieldName)
{ {
if ( ! isset($this->_owningTableMap[$fieldName])) { if ( ! isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName])) { if (isset($this->_class->associationMappings[$fieldName])) {
if (isset($this->_class->inheritedAssociationFields[$fieldName])) { if (isset($this->_class->inheritedAssociationFields[$fieldName])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->inheritedAssociationFields[$fieldName])->primaryTable['name']; $this->_class->inheritedAssociationFields[$fieldName])->primaryTable['name'];
} else { } else {
$this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name']; $this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name'];
} }
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->fieldMappings[$fieldName]['inherited'])->primaryTable['name']; $this->_class->fieldMappings[$fieldName]['inherited'])->primaryTable['name'];
} else { } else {
$this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name']; $this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name'];
} }
} }
return $this->_owningTableMap[$fieldName]; return $this->_owningTableMap[$fieldName];
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
*/ */
public function executeInserts() public function executeInserts()
{ {
if ( ! $this->_queuedInserts) { if ( ! $this->_queuedInserts) {
return; return;
} }
$postInsertIds = array(); $postInsertIds = array();
$idGen = $this->_class->idGenerator; $idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator(); $isPostInsertId = $idGen->isPostInsertGenerator();
$sqlLogger = $this->_conn->getConfiguration()->getSqlLogger();
// Prepare statements for all tables // Prepare statements for all tables
$stmts = $classes = array(); $stmts = $classes = array();
$stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->_class->insertSql); $stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->_class->insertSql);
$classes[$this->_class->name] = $this->_class; $sql[$this->_class->primaryTable['name']] = $this->_class->insertSql;
foreach ($this->_class->parentClasses as $parentClass) { foreach ($this->_class->parentClasses as $parentClass) {
$classes[$parentClass] = $this->_em->getClassMetadata($parentClass); $parentClass = $this->_em->getClassMetadata($parentClass);
$stmts[$classes[$parentClass]->primaryTable['name']] = $this->_conn->prepare($classes[$parentClass]->insertSql); $sql[$parentClass->primaryTable['name']] = $parentClass->insertSql;
} $stmts[$parentClass->primaryTable['name']] = $this->_conn->prepare($parentClass->insertSql);
$rootTableName = $classes[$this->_class->rootEntityName]->primaryTable['name']; }
$rootTableName = $this->_em->getClassMetadata($this->_class->rootEntityName)->primaryTable['name'];
foreach ($this->_queuedInserts as $entity) { foreach ($this->_queuedInserts as $entity) {
$insertData = array(); $insertData = array();
$this->_prepareData($entity, $insertData, true); $this->_prepareData($entity, $insertData, true);
// Execute insert on root table
$paramIndex = 1;
$stmt = $stmts[$rootTableName];
foreach ($insertData[$rootTableName] as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
$stmt->execute();
unset($insertData[$rootTableName]);
if ($isPostInsertId) { // Execute insert on root table
$id = $idGen->generate($this->_em, $entity); $stmt = $stmts[$rootTableName];
$postInsertIds[$id] = $entity; $paramIndex = 1;
} else { if ($sqlLogger) {
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); $params = array();
} foreach ($insertData[$rootTableName] as $columnName => $value) {
$params[$paramIndex] = $value;
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
$sqlLogger->logSql($sql[$rootTableName], $params);
} else {
foreach ($insertData[$rootTableName] as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
}
$stmt->execute();
unset($insertData[$rootTableName]);
// Execute inserts on subtables if ($isPostInsertId) {
foreach ($insertData as $tableName => $data) { $id = $idGen->generate($this->_em, $entity);
$stmt = $stmts[$tableName]; $postInsertIds[$id] = $entity;
$paramIndex = 1; } else {
foreach ((array)$id as $idVal) { $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/); }
}
foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
$stmt->execute();
}
}
foreach ($stmts as $stmt) // Execute inserts on subtables
$stmt->closeCursor(); foreach ($insertData as $tableName => $data) {
$stmt = $stmts[$tableName];
$paramIndex = 1;
if ($sqlLogger) {
//TODO: Log type
$params = array();
foreach ((array)$id as $idVal) {
$params[$paramIndex] = $idVal;
$stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/);
}
foreach ($data as $columnName => $value) {
$params[$paramIndex] = $value;
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
$sqlLogger->logSql($sql[$tableName], $params);
} else {
foreach ((array)$id as $idVal) {
$stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/);
}
foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
}
}
$stmt->execute();
}
}
$this->_queuedInserts = array(); foreach ($stmts as $stmt)
$stmt->closeCursor();
return $postInsertIds; $this->_queuedInserts = array();
}
/** return $postInsertIds;
* Updates an entity. }
*
* @param object $entity The entity to update.
* @override
*/
public function update($entity)
{
$updateData = array();
$this->_prepareData($entity, $updateData);
$id = array_combine( /**
$this->_class->getIdentifierFieldNames(), * Updates an entity.
$this->_em->getUnitOfWork()->getEntityIdentifier($entity) *
); * @param object $entity The entity to update.
* @override
*/
public function update($entity)
{
$updateData = array();
$this->_prepareData($entity, $updateData);
foreach ($updateData as $tableName => $data) { $id = array_combine(
$this->_conn->update($tableName, $updateData[$tableName], $id); $this->_class->getIdentifierFieldNames(),
} $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
} );
/** foreach ($updateData as $tableName => $data) {
* Deletes an entity. $this->_conn->update($tableName, $updateData[$tableName], $id);
* }
* @param object $entity The entity to delete. }
* @override
*/
public function delete($entity)
{
$id = array_combine(
$this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
);
// If the database platform supports FKs, just /**
// delete the row from the root table. Cascades do the rest. * Deletes an entity.
if ($this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) { *
$this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName) * @param object $entity The entity to delete.
->primaryTable['name'], $id); * @override
} else { */
// Delete the parent tables, starting from this class' table up to the root table public function delete($entity)
$this->_conn->delete($this->_class->primaryTable['name'], $id); {
foreach ($this->_class->parentClasses as $parentClass) { $id = array_combine(
$this->_conn->delete($this->_em->getClassMetadata($parentClass)->primaryTable['name'], $id); $this->_class->getIdentifierFieldNames(),
} $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
} );
}
// If the database platform supports FKs, just
/** // delete the row from the root table. Cascades do the rest.
* Gets the SELECT SQL to select a single entity by a set of field criteria. if ($this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) {
* $this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName)
* @param array $criteria ->primaryTable['name'], $id);
* @return string The SQL. } else {
* @todo Quote identifier. // Delete the parent tables, starting from this class' table up to the root table
* @override $this->_conn->delete($this->_class->primaryTable['name'], $id);
*/ foreach ($this->_class->parentClasses as $parentClass) {
protected function _getSelectSingleEntitySql(array $criteria) $this->_conn->delete($this->_em->getClassMetadata($parentClass)->primaryTable['name'], $id);
{ }
$tableAliases = array(); }
$aliasIndex = 1; }
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = 't0'; /**
* Gets the SELECT SQL to select a single entity by a set of field criteria.
foreach (array_merge($this->_class->subClasses, $this->_class->parentClasses) as $className) { *
$tableAliases[$className] = 't' . $aliasIndex++; * @param array $criteria
} * @return string The SQL.
* @todo Quote identifier.
$columnList = ''; * @override
foreach ($this->_class->fieldMappings as $fieldName => $mapping) { */
$tableAlias = isset($mapping['inherited']) ? protected function _getSelectSingleEntitySql(array $criteria)
$tableAliases[$mapping['inherited']] : $baseTableAlias; {
if ($columnList != '') $columnList .= ', '; $tableAliases = array();
$columnList .= $tableAlias . '.' . $this->_class->columnNames[$fieldName]; $aliasIndex = 1;
} $idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = 't0';
$sql = 'SELECT ' . $columnList . ' FROM ' . $this->_class->primaryTable['name']. ' ' . $baseTableAlias;
foreach (array_merge($this->_class->subClasses, $this->_class->parentClasses) as $className) {
// INNER JOIN parent tables $tableAliases[$className] = 't' . $aliasIndex++;
foreach ($this->_class->parentClasses as $parentClassName) { }
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $tableAliases[$parentClassName]; $columnList = '';
$sql .= ' INNER JOIN ' . $parentClass->primaryTable['name'] . ' ' . $tableAlias . ' ON '; foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
$first = true; $tableAlias = isset($mapping['inherited']) ?
foreach ($idColumns as $idColumn) { $tableAliases[$mapping['inherited']] : $baseTableAlias;
if ($first) $first = false; else $sql .= ' AND '; if ($columnList != '') $columnList .= ', ';
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $columnList .= $tableAlias . '.' . $this->_class->columnNames[$fieldName];
} }
}
$sql = 'SELECT ' . $columnList . ' FROM ' . $this->_class->primaryTable['name']. ' ' . $baseTableAlias;
// OUTER JOIN sub tables
foreach ($this->_class->subClasses as $subClassName) { // INNER JOIN parent tables
$subClass = $this->_em->getClassMetadata($subClassName); foreach ($this->_class->parentClasses as $parentClassName) {
$tableAlias = $tableAliases[$subClassName]; $parentClass = $this->_em->getClassMetadata($parentClassName);
$sql .= ' LEFT JOIN ' . $subClass->primaryTable['name'] . ' ' . $tableAlias . ' ON '; $tableAlias = $tableAliases[$parentClassName];
$first = true; $sql .= ' INNER JOIN ' . $parentClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
foreach ($idColumns as $idColumn) { $first = true;
if ($first) $first = false; else $sql .= ' AND '; foreach ($idColumns as $idColumn) {
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; if ($first) $first = false; else $sql .= ' AND ';
} $sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
}
$conditionSql = '';
foreach ($criteria as $field => $value) { // OUTER JOIN sub tables
if ($conditionSql != '') $conditionSql .= ' AND '; foreach ($this->_class->subClasses as $subClassName) {
$conditionSql .= $baseTableAlias . '.' . $this->_class->columnNames[$field] . ' = ?'; $subClass = $this->_em->getClassMetadata($subClassName);
} $tableAlias = $tableAliases[$subClassName];
$sql .= ' LEFT JOIN ' . $subClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
return $sql . ' WHERE ' . $conditionSql; $first = true;
} foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') $conditionSql .= ' AND ';
$conditionSql .= $baseTableAlias . '.' . $this->_class->columnNames[$field] . ' = ?';
}
return $sql . ' WHERE ' . $conditionSql;
}
} }
\ No newline at end of file
<?php <?php
/* /*
* $Id$ * $Id$
* *
...@@ -37,336 +37,349 @@ use Doctrine\ORM\Mapping\ClassMetadata; ...@@ -37,336 +37,349 @@ use Doctrine\ORM\Mapping\ClassMetadata;
* @since 2.0 * @since 2.0
*/ */
class StandardEntityPersister class StandardEntityPersister
{ {
/** /**
* Metadata object that describes the mapping of the mapped entity class. * Metadata object that describes the mapping of the mapped entity class.
* *
* @var Doctrine\ORM\Mapping\ClassMetadata * @var Doctrine\ORM\Mapping\ClassMetadata
*/ */
protected $_class; protected $_class;
/** /**
* The name of the entity the persister is used for. * The name of the entity the persister is used for.
* *
* @var string * @var string
*/ */
protected $_entityName; protected $_entityName;
/** /**
* The Connection instance. * The Connection instance.
* *
* @var Doctrine\DBAL\Connection $conn * @var Doctrine\DBAL\Connection $conn
*/ */
protected $_conn; protected $_conn;
/** /**
* The EntityManager instance. * The EntityManager instance.
* *
* @var Doctrine\ORM\EntityManager * @var Doctrine\ORM\EntityManager
*/ */
protected $_em; protected $_em;
/** /**
* Queued inserts. * Queued inserts.
* *
* @var array * @var array
*/ */
protected $_queuedInserts = array(); protected $_queuedInserts = array();
/** /**
* Initializes a new instance of a class derived from AbstractEntityPersister * Initializes a new instance of a class derived from AbstractEntityPersister
* that uses the given EntityManager and persists instances of the class described * that uses the given EntityManager and persists instances of the class described
* by the given class metadata descriptor. * by the given class metadata descriptor.
*/ */
public function __construct(EntityManager $em, ClassMetadata $class) public function __construct(EntityManager $em, ClassMetadata $class)
{ {
$this->_em = $em; $this->_em = $em;
$this->_entityName = $class->name; $this->_entityName = $class->name;
$this->_conn = $em->getConnection(); $this->_conn = $em->getConnection();
$this->_class = $class; $this->_class = $class;
} }
/** /**
* Adds an entity to the queued inserts. * Adds an entity to the queued inserts.
* *
* @param object $entity * @param object $entity
*/ */
public function addInsert($entity) public function addInsert($entity)
{ {
$this->_queuedInserts[spl_object_hash($entity)] = $entity; $this->_queuedInserts[spl_object_hash($entity)] = $entity;
} }
/** /**
* Executes all queued inserts. * Executes all queued inserts.
* *
* @return array An array of any generated post-insert IDs. * @return array An array of any generated post-insert IDs.
*/ */
public function executeInserts() public function executeInserts()
{ {
if ( ! $this->_queuedInserts) { if ( ! $this->_queuedInserts) {
return; return;
} }
$postInsertIds = array(); $postInsertIds = array();
$idGen = $this->_class->idGenerator; $idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator(); $isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->_class->insertSql); $stmt = $this->_conn->prepare($this->_class->insertSql);
$primaryTableName = $this->_class->primaryTable['name']; $primaryTableName = $this->_class->primaryTable['name'];
foreach ($this->_queuedInserts as $entity) { $sqlLogger = $this->_conn->getConfiguration()->getSqlLogger();
$insertData = array();
$this->_prepareData($entity, $insertData, true); foreach ($this->_queuedInserts as $entity) {
$insertData = array();
$paramIndex = 1; $this->_prepareData($entity, $insertData, true);
foreach ($insertData[$primaryTableName] as $value) {
$stmt->bindValue($paramIndex++, $value/*, Type::getType()*/);
}
$stmt->execute();
if ($isPostInsertId) {
$postInsertIds[$idGen->generate($this->_em, $entity)] = $entity;
}
}
$stmt->closeCursor();
$this->_queuedInserts = array();
return $postInsertIds;
}
/**
* Updates an entity.
*
* @param object $entity The entity to update.
*/
public function update($entity)
{
$updateData = array();
$this->_prepareData($entity, $updateData);
$id = array_combine($this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity));
$tableName = $this->_class->primaryTable['name'];
$this->_conn->update($tableName, $updateData[$tableName], $id);
}
/**
* Deletes an entity.
*
* @param object $entity The entity to delete.
*/
public function delete($entity)
{
$id = array_combine(
$this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
);
$this->_conn->delete($this->_class->primaryTable['name'], $id);
}
/**
* Adds an entity to delete.
*
* @param object $entity
*/
public function addDelete($entity)
{
}
/**
* Executes all pending entity deletions.
*
* @see addDelete()
*/
public function executeDeletions()
{
}
/**
* Gets the ClassMetadata instance of the entity class this persister is used for.
*
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
public function getClassMetadata()
{
return $this->_class;
}
/**
* Gets the table name to use for temporary identifier tables.
*/
public function getTemporaryIdTableName()
{
//...
}
/** $paramIndex = 1;
* Prepares the data changeset of an entity for database insertion. if ($sqlLogger) {
* The array that is passed as the second parameter is filled with //TODO: Log type
* <columnName> => <value> pairs, grouped by table name, during this preparation. $params = array();
* foreach ($insertData[$primaryTableName] as $value) {
* Example: $params[$paramIndex] = $value;
* <code> $stmt->bindValue($paramIndex++, $value/*, Type::getType()*/);
* array( }
* 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...), $sqlLogger->logSql($this->_class->insertSql, $params);
* 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...), } else {
* ... foreach ($insertData[$primaryTableName] as $value) {
* ) $stmt->bindValue($paramIndex++, $value/*, Type::getType()*/);
* </code> }
* }
* Notes to inheritors: Be sure to call <code>parent::_prepareData($entity, $result, $isInsert);</code>
* $stmt->execute();
* @param object $entity
* @param array $result The reference to the data array. if ($isPostInsertId) {
* @param boolean $isInsert $postInsertIds[$idGen->generate($this->_em, $entity)] = $entity;
*/ }
protected function _prepareData($entity, array &$result, $isInsert = false) }
{
$platform = $this->_conn->getDatabasePlatform(); $stmt->closeCursor();
$uow = $this->_em->getUnitOfWork(); $this->_queuedInserts = array();
foreach ($uow->getEntityChangeSet($entity) as $field => $change) { return $postInsertIds;
$oldVal = $change[0]; }
$newVal = $change[1];
/**
$columnName = $this->_class->getColumnName($field); * Updates an entity.
*
if (isset($this->_class->associationMappings[$field])) { * @param object $entity The entity to update.
$assocMapping = $this->_class->associationMappings[$field]; */
// Only owning side of x-1 associations can have a FK column. public function update($entity)
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) { {
continue; $updateData = array();
} $this->_prepareData($entity, $updateData);
$id = array_combine($this->_class->getIdentifierFieldNames(),
// Special case: One-one self-referencing of the same class. $this->_em->getUnitOfWork()->getEntityIdentifier($entity));
if ($newVal !== null && $assocMapping->sourceEntityName == $assocMapping->targetEntityName) { $tableName = $this->_class->primaryTable['name'];
$oid = spl_object_hash($newVal); $this->_conn->update($tableName, $updateData[$tableName], $id);
$isScheduledForInsert = $uow->isRegisteredNew($newVal); }
if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
// The associated entity $newVal is not yet persisted, so we must /**
// set $newVal = null, in order to insert a null value and update later. * Deletes an entity.
$newVal = null; *
} else if ($isInsert && ! $isScheduledForInsert && $uow->getEntityState($newVal) == UnitOfWork::STATE_MANAGED) { * @param object $entity The entity to delete.
// $newVal is already fully persisted */
// Clear changeset of $newVal, so that only the identifier is updated. public function delete($entity)
// Not sure this is really rock-solid here but it seems to work. {
$uow->clearEntityChangeSet($oid); $id = array_combine(
$uow->propertyChanged($newVal, $field, $entity, $entity); $this->_class->getIdentifierFieldNames(),
} $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
} );
$this->_conn->delete($this->_class->primaryTable['name'], $id);
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { }
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
if ($newVal === null) { /**
$result[$this->getOwningTable($field)][$sourceColumn] = null; * Adds an entity to delete.
} else { *
$result[$this->getOwningTable($field)][$sourceColumn] = * @param object $entity
$otherClass->reflFields[$otherClass->fieldNames[$targetColumn]] */
->getValue($newVal); public function addDelete($entity)
} {
}
} else if ($newVal === null) { }
$result[$this->getOwningTable($field)][$columnName] = null;
} else { /**
$result[$this->getOwningTable($field)][$columnName] = Type::getType( * Executes all pending entity deletions.
$this->_class->fieldMappings[$field]['type']) *
->convertToDatabaseValue($newVal, $platform); * @see addDelete()
} */
} public function executeDeletions()
} {
/** }
* Gets the name of the table that owns the column the given field is mapped to.
* /**
* @param string $fieldName * Gets the ClassMetadata instance of the entity class this persister is used for.
* @return string *
*/ * @return Doctrine\ORM\Mapping\ClassMetadata
public function getOwningTable($fieldName) */
{ public function getClassMetadata()
return $this->_class->primaryTable['name']; {
} return $this->_class;
}
/**
* Loads an entity by a list of field criteria. /**
* * Gets the table name to use for temporary identifier tables.
* @param array $criteria The criteria by which to load the entity. */
* @param object $entity The entity to load the data into. If not specified, public function getTemporaryIdTableName()
* a new entity is created. {
*/ //...
public function load(array $criteria, $entity = null) }
{
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria)); /**
$stmt->execute(array_values($criteria)); * Prepares the data changeset of an entity for database insertion.
$data = array(); * The array that is passed as the second parameter is filled with
foreach ($stmt->fetch(\PDO::FETCH_ASSOC) as $column => $value) { * <columnName> => <value> pairs, grouped by table name, during this preparation.
$fieldName = $this->_class->fieldNames[$column]; *
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName)) * Example:
->convertToPHPValue($value); * <code>
} * array(
$stmt->closeCursor(); * 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...),
* 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...),
if ($entity === null) { * ...
$entity = $this->_em->getUnitOfWork()->createEntity($this->_entityName, $data); * )
} else { * </code>
foreach ($data as $field => $value) { *
$this->_class->reflFields[$field]->setValue($entity, $value); * Notes to inheritors: Be sure to call <code>parent::_prepareData($entity, $result, $isInsert);</code>
} *
$id = array(); * @param object $entity
if ($this->_class->isIdentifierComposite) { * @param array $result The reference to the data array.
foreach ($this->_class->identifier as $fieldName) { * @param boolean $isInsert
$id[] = $data[$fieldName]; */
} protected function _prepareData($entity, array &$result, $isInsert = false)
} else { {
$id = array($data[$this->_class->getSingleIdentifierFieldName()]); $platform = $this->_conn->getDatabasePlatform();
} $uow = $this->_em->getUnitOfWork();
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
} foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
$oldVal = $change[0];
if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) { $newVal = $change[1];
foreach ($this->_class->associationMappings as $field => $assoc) {
if ($assoc->isOneToOne()) { $columnName = $this->_class->getColumnName($field);
if ($assoc->isLazilyFetched()) {
// Inject proxy if (isset($this->_class->associationMappings[$field])) {
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc); $assocMapping = $this->_class->associationMappings[$field];
$this->_class->reflFields[$field]->setValue($entity, $proxy); // Only owning side of x-1 associations can have a FK column.
} else { if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//TODO: Eager fetch? continue;
} }
} else {
// Inject collection // Special case: One-one self-referencing of the same class.
$this->_class->reflFields[$field]->setValue( if ($newVal !== null && $assocMapping->sourceEntityName == $assocMapping->targetEntityName) {
$entity, new PersistentCollection($this->_em, $oid = spl_object_hash($newVal);
$this->_em->getClassMetadata($assoc->targetEntityName) $isScheduledForInsert = $uow->isRegisteredNew($newVal);
)); if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
} // The associated entity $newVal is not yet persisted, so we must
} // set $newVal = null, in order to insert a null value and update later.
} $newVal = null;
} else if ($isInsert && ! $isScheduledForInsert && $uow->getEntityState($newVal) == UnitOfWork::STATE_MANAGED) {
return $entity; // $newVal is already fully persisted
} // Clear changeset of $newVal, so that only the identifier is updated.
// Not sure this is really rock-solid here but it seems to work.
/** $uow->clearEntityChangeSet($oid);
* Gets the SELECT SQL to select a single entity by a set of field criteria. $uow->propertyChanged($newVal, $field, $entity, $entity);
* }
* @param array $criteria }
* @return string The SQL.
* @todo Quote identifier. foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
*/ $otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
protected function _getSelectSingleEntitySql(array $criteria) if ($newVal === null) {
{ $result[$this->getOwningTable($field)][$sourceColumn] = null;
$columnList = ''; } else {
foreach ($this->_class->columnNames as $column) { $result[$this->getOwningTable($field)][$sourceColumn] =
if ($columnList != '') $columnList .= ', '; $otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]->getValue($newVal);
$columnList .= $column; }
} }
} else if ($newVal === null) {
$conditionSql = ''; $result[$this->getOwningTable($field)][$columnName] = null;
foreach ($criteria as $field => $value) { } else {
if ($conditionSql != '') $conditionSql .= ' AND '; $result[$this->getOwningTable($field)][$columnName] = Type::getType(
$conditionSql .= $this->_class->columnNames[$field] . ' = ?'; $this->_class->fieldMappings[$field]['type'])
} ->convertToDatabaseValue($newVal, $platform);
}
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName() }
. ' WHERE ' . $conditionSql; }
}
/**
* Gets the name of the table that owns the column the given field is mapped to.
*
* @param string $fieldName
* @return string
*/
public function getOwningTable($fieldName)
{
return $this->_class->primaryTable['name'];
}
/**
* Loads an entity by a list of field criteria.
*
* @param array $criteria The criteria by which to load the entity.
* @param object $entity The entity to load the data into. If not specified,
* a new entity is created.
*/
public function load(array $criteria, $entity = null)
{
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria));
$stmt->execute(array_values($criteria));
$data = array();
foreach ($stmt->fetch(\PDO::FETCH_ASSOC) as $column => $value) {
$fieldName = $this->_class->fieldNames[$column];
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
->convertToPHPValue($value);
}
$stmt->closeCursor();
if ($entity === null) {
$entity = $this->_em->getUnitOfWork()->createEntity($this->_entityName, $data);
} else {
foreach ($data as $field => $value) {
$this->_class->reflFields[$field]->setValue($entity, $value);
}
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[] = $data[$fieldName];
}
} else {
$id = array($data[$this->_class->getSingleIdentifierFieldName()]);
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) {
foreach ($this->_class->associationMappings as $field => $assoc) {
if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched) {
// Inject proxy
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc);
$this->_class->reflFields[$field]->setValue($entity, $proxy);
} else {
//TODO: Eager fetch?
}
} else {
// Inject collection
$this->_class->reflFields[$field]->setValue(
$entity, new PersistentCollection($this->_em,
$this->_em->getClassMetadata($assoc->targetEntityName)
));
}
}
}
return $entity;
}
/**
* Gets the SELECT SQL to select a single entity by a set of field criteria.
*
* @param array $criteria
* @return string The SQL.
* @todo Quote identifier.
*/
protected function _getSelectSingleEntitySql(array $criteria)
{
$columnList = '';
foreach ($this->_class->columnNames as $column) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $column;
}
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') $conditionSql .= ' AND ';
$conditionSql .= $this->_class->columnNames[$field] . ' = ?';
}
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
. ' WHERE ' . $conditionSql;
}
} }
\ No newline at end of file
...@@ -56,9 +56,8 @@ class ResultSetMapping ...@@ -56,9 +56,8 @@ class ResultSetMapping
/** /**
* *
* @param <type> $class * @param string $class The class name.
* @param <type> $alias The alias for this class. The alias must be unique within this ResultSetMapping. * @param string $alias The alias for this class. The alias must be unique within this ResultSetMapping.
* @param <type> $discriminatorColumn
*/ */
public function addEntityResult($class, $alias) public function addEntityResult($class, $alias)
{ {
...@@ -67,9 +66,8 @@ class ResultSetMapping ...@@ -67,9 +66,8 @@ class ResultSetMapping
/** /**
* *
* @param <type> $className * @param string $alias
* @param <type> $alias * @param string $discrColumn
* @param <type> $discrColumn
*/ */
public function setDiscriminatorColumn($alias, $discrColumn) public function setDiscriminatorColumn($alias, $discrColumn)
{ {
...@@ -130,9 +128,9 @@ class ResultSetMapping ...@@ -130,9 +128,9 @@ class ResultSetMapping
/** /**
* *
* @param <type> $alias * @param string $alias
* @param <type> $columnName * @param string $columnName
* @param <type> $fieldName * @param string $fieldName
*/ */
public function addFieldResult($alias, $columnName, $fieldName) public function addFieldResult($alias, $columnName, $fieldName)
{ {
...@@ -145,10 +143,10 @@ class ResultSetMapping ...@@ -145,10 +143,10 @@ class ResultSetMapping
/** /**
* *
* @param <type> $class * @param string $class
* @param <type> $alias * @param string $alias
* @param <type> $parentAlias * @param string $parentAlias
* @param <type> $relation * @param object $relation
*/ */
public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)
{ {
...@@ -156,12 +154,12 @@ class ResultSetMapping ...@@ -156,12 +154,12 @@ class ResultSetMapping
$this->parentAliasMap[$alias] = $parentAlias; $this->parentAliasMap[$alias] = $parentAlias;
$this->relationMap[$alias] = $relation; $this->relationMap[$alias] = $relation;
} }
/*public function isDiscriminatorColumn($columnName) /**
{ *
return isset($this->_discriminatorMap[$columnName]); * @param string $columnName
}*/ * @param string $alias
*/
public function addScalarResult($columnName, $alias) public function addScalarResult($columnName, $alias)
{ {
$this->scalarMappings[$columnName] = $alias; $this->scalarMappings[$columnName] = $alias;
......
...@@ -483,7 +483,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -483,7 +483,6 @@ class UnitOfWork implements PropertyChangedListener
} }
if ( ! $assoc->isCascadeSave) { if ( ! $assoc->isCascadeSave) {
//echo "NOT CASCADING INTO " . $assoc->getSourceFieldName() . PHP_EOL;
return; // "Persistence by reachability" only if save cascade specified return; // "Persistence by reachability" only if save cascade specified
} }
...@@ -498,7 +497,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -498,7 +497,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entry); $oid = spl_object_hash($entry);
if ($state == self::STATE_NEW) { if ($state == self::STATE_NEW) {
// Get identifier, if possible (not post-insert) // Get identifier, if possible (not post-insert)
$idGen = $targetClass->getIdGenerator(); $idGen = $targetClass->idGenerator;
if ( ! $idGen->isPostInsertGenerator()) { if ( ! $idGen->isPostInsertGenerator()) {
$idValue = $idGen->generate($this->_em, $entry); $idValue = $idGen->generate($this->_em, $entry);
$this->_entityStates[$oid] = self::STATE_MANAGED; $this->_entityStates[$oid] = self::STATE_MANAGED;
...@@ -803,35 +802,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -803,35 +802,6 @@ class UnitOfWork implements PropertyChangedListener
isset($this->_entityDeletions[$oid]); isset($this->_entityDeletions[$oid]);
} }
/**
* Detaches all currently managed entities.
* Alternatively, if an entity class name is given, all entities of that type
* (or subtypes) are detached. Don't forget that entities are registered in
* the identity map with the name of the root entity class. So calling detachAll()
* with a class name that is not the name of a root entity has no effect.
*
* @return integer The number of detached entities.
*/
public function detachAll($entityName = null)
{
$numDetached = 0;
if ($entityName !== null && isset($this->_identityMap[$entityName])) {
$numDetached = count($this->_identityMap[$entityName]);
foreach ($this->_identityMap[$entityName] as $entity) {
$this->detach($entity);
}
$this->_identityMap[$entityName] = array();
} else {
$numDetached = count($this->_identityMap);
$this->_identityMap = array();
$this->_entityInsertions = array();
$this->_entityUpdates = array();
$this->_entityDeletions = array();
}
return $numDetached;
}
/** /**
* Registers an entity in the identity map. * Registers an entity in the identity map.
* Note that entities in a hierarchy are registered with the class name of * Note that entities in a hierarchy are registered with the class name of
...@@ -960,8 +930,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -960,8 +930,7 @@ class UnitOfWork implements PropertyChangedListener
return isset($this->_identityMap return isset($this->_identityMap
[$classMetadata->rootEntityName] [$classMetadata->rootEntityName]
[$idHash] [$idHash]);
);
} }
/** /**
...@@ -1174,6 +1143,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1174,6 +1143,10 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Cascades a merge operation to associated entities. * Cascades a merge operation to associated entities.
*
* @param object $entity
* @param object $managedCopy
* @param array $visited
*/ */
private function _cascadeMerge($entity, $managedCopy, array &$visited) private function _cascadeMerge($entity, $managedCopy, array &$visited)
{ {
...@@ -1199,6 +1172,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1199,6 +1172,7 @@ class UnitOfWork implements PropertyChangedListener
* *
* @param object $entity * @param object $entity
* @param array $visited * @param array $visited
* @param array $insertNow
*/ */
private function _cascadeSave($entity, array &$visited, array &$insertNow) private function _cascadeSave($entity, array &$visited, array &$insertNow)
{ {
...@@ -1223,6 +1197,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1223,6 +1197,7 @@ class UnitOfWork implements PropertyChangedListener
* Cascades the delete operation to associated entities. * Cascades the delete operation to associated entities.
* *
* @param object $entity * @param object $entity
* @param array $visited
*/ */
private function _cascadeDelete($entity, array &$visited) private function _cascadeDelete($entity, array &$visited)
{ {
......
...@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; ...@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Functional\Schema;
use Doctrine\Tests\TestUtil; use Doctrine\Tests\TestUtil;
use Doctrine\DBAL\Schema; use Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
require_once __DIR__ . '/../../../TestInit.php'; require_once __DIR__ . '/../../../TestInit.php';
...@@ -59,13 +60,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -59,13 +60,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -86,13 +87,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -86,13 +87,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -114,13 +115,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -114,13 +115,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -158,13 +159,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -158,13 +159,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -197,13 +198,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -197,13 +198,13 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -245,7 +246,7 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -245,7 +246,7 @@ class MysqlSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->_sm->createView('test_create_view', 'SELECT * from mysql.user'); $this->_sm->createView('test_create_view', 'SELECT * from mysql.user');
$views = $this->_sm->listViews(); $views = $this->_sm->listViews();
$this->assertEquals($views[0]['name'], 'test_create_view'); $this->assertEquals('test_create_view', $views[0]['name']);
$this->assertEquals($views[0]['sql'], '/* ALGORITHM=UNDEFINED */ select `mysql`.`user`.`Host` AS `Host`,`mysql`.`user`.`User` AS `User`,`mysql`.`user`.`Password` AS `Password`,`mysql`.`user`.`Select_priv` AS `Select_priv`,`mysql`.`user`.`Insert_priv` AS `Insert_priv`,`mysql`.`user`.`Update_priv` AS `Update_priv`,`mysql`.`user`.`Delete_priv` AS `Delete_priv`,`mysql`.`user`.`Create_priv` AS `Create_priv`,`mysql`.`user`.`Drop_priv` AS `Drop_priv`,`mysql`.`user`.`Reload_priv` AS `Reload_priv`,`mysql`.`user`.`Shutdown_priv` AS `Shutdown_priv`,`mysql`.`user`.`Process_priv` AS `Process_priv`,`mysql`.`user`.`File_priv` AS `File_priv`,`mysql`.`user`.`Grant_priv` AS `Grant_priv`,`mysql`.`user`.`References_priv` AS `References_priv`,`mysql`.`user`.`Index_priv` AS `Index_priv`,`mysql`.`user`.`Alter_priv` AS `Alter_priv`,`mysql`.`user`.`Show_db_priv` AS `Show_db_priv`,`mysql`.`user`.`Super_priv` AS `Super_priv`,`mysql`.`user`.`Create_tmp_table_priv` AS `Create_tmp_table_priv`,`mysql`.`user`.`Lock_tables_priv` AS `Lock_tables_priv`,`mysql`.`user`.`Execute_priv` AS `Execute_priv`,`mysql`.`user`.`Repl_slave_priv` AS `Repl_slave_priv`,`mysql`.`user`.`Repl_client_priv` AS `Repl_client_priv`,`mysql`.`user`.`Create_view_priv` AS `Create_view_priv`,`mysql`.`user`.`Show_view_priv` AS `Show_view_priv`,`mysql`.`user`.`Create_routine_priv` AS `Create_routine_priv`,`mysql`.`user`.`Alter_routine_priv` AS `Alter_routine_priv`,`mysql`.`user`.`Create_user_priv` AS `Create_user_priv`,`mysql`.`user`.`ssl_type` AS `ssl_type`,`mysql`.`user`.`ssl_cipher` AS `ssl_cipher`,`mysql`.`user`.`x509_issuer` AS `x509_issuer`,`mysql`.`user`.`x509_subject` AS `x509_subject`,`mysql`.`user`.`max_questions` AS `max_questions`,`mysql`.`user`.`max_updates` AS `max_updates`,`mysql`.`user`.`max_connections` AS `max_connections`,`mysql`.`user`.`max_user_connections` AS `max_user_connections` from `mysql`.`user`'); $this->assertEquals('/* ALGORITHM=UNDEFINED */ select `mysql`.`user`.`Host` AS `Host`,`mysql`.`user`.`User` AS `User`,`mysql`.`user`.`Password` AS `Password`,`mysql`.`user`.`Select_priv` AS `Select_priv`,`mysql`.`user`.`Insert_priv` AS `Insert_priv`,`mysql`.`user`.`Update_priv` AS `Update_priv`,`mysql`.`user`.`Delete_priv` AS `Delete_priv`,`mysql`.`user`.`Create_priv` AS `Create_priv`,`mysql`.`user`.`Drop_priv` AS `Drop_priv`,`mysql`.`user`.`Reload_priv` AS `Reload_priv`,`mysql`.`user`.`Shutdown_priv` AS `Shutdown_priv`,`mysql`.`user`.`Process_priv` AS `Process_priv`,`mysql`.`user`.`File_priv` AS `File_priv`,`mysql`.`user`.`Grant_priv` AS `Grant_priv`,`mysql`.`user`.`References_priv` AS `References_priv`,`mysql`.`user`.`Index_priv` AS `Index_priv`,`mysql`.`user`.`Alter_priv` AS `Alter_priv`,`mysql`.`user`.`Show_db_priv` AS `Show_db_priv`,`mysql`.`user`.`Super_priv` AS `Super_priv`,`mysql`.`user`.`Create_tmp_table_priv` AS `Create_tmp_table_priv`,`mysql`.`user`.`Lock_tables_priv` AS `Lock_tables_priv`,`mysql`.`user`.`Execute_priv` AS `Execute_priv`,`mysql`.`user`.`Repl_slave_priv` AS `Repl_slave_priv`,`mysql`.`user`.`Repl_client_priv` AS `Repl_client_priv`,`mysql`.`user`.`Create_view_priv` AS `Create_view_priv`,`mysql`.`user`.`Show_view_priv` AS `Show_view_priv`,`mysql`.`user`.`Create_routine_priv` AS `Create_routine_priv`,`mysql`.`user`.`Alter_routine_priv` AS `Alter_routine_priv`,`mysql`.`user`.`Create_user_priv` AS `Create_user_priv`,`mysql`.`user`.`ssl_type` AS `ssl_type`,`mysql`.`user`.`ssl_cipher` AS `ssl_cipher`,`mysql`.`user`.`x509_issuer` AS `x509_issuer`,`mysql`.`user`.`x509_subject` AS `x509_subject`,`mysql`.`user`.`max_questions` AS `max_questions`,`mysql`.`user`.`max_updates` AS `max_updates`,`mysql`.`user`.`max_connections` AS `max_connections`,`mysql`.`user`.`max_user_connections` AS `max_user_connections` from `mysql`.`user`', $views[0]['sql']);
} }
} }
\ No newline at end of file
...@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; ...@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Functional\Schema;
use Doctrine\Tests\TestUtil; use Doctrine\Tests\TestUtil;
use Doctrine\DBAL\Schema; use Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
require_once __DIR__ . '/../../../TestInit.php'; require_once __DIR__ . '/../../../TestInit.php';
...@@ -58,13 +59,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -58,13 +59,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -91,13 +92,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -91,13 +92,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -131,13 +132,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -131,13 +132,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -164,13 +165,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -164,13 +165,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -198,13 +199,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -198,13 +199,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -250,13 +251,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -250,13 +251,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
...@@ -314,13 +315,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -314,13 +315,13 @@ class SqliteSchemaManagerTest extends \Doctrine\Tests\DbalFunctionalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace Doctrine\Tests\DBAL\Platforms; namespace Doctrine\Tests\DBAL\Platforms;
use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Types\Type;
require_once __DIR__ . '/../../TestInit.php'; require_once __DIR__ . '/../../TestInit.php';
...@@ -19,13 +20,13 @@ class SqlitePlatformTest extends \Doctrine\Tests\DbalTestCase ...@@ -19,13 +20,13 @@ class SqlitePlatformTest extends \Doctrine\Tests\DbalTestCase
{ {
$columns = array( $columns = array(
'id' => array( 'id' => array(
'type' => new \Doctrine\DBAL\Types\IntegerType, 'type' => Type::getType('integer'),
'autoincrement' => true, 'autoincrement' => true,
'primary' => true, 'primary' => true,
'notnull' => true 'notnull' => true
), ),
'test' => array( 'test' => array(
'type' => new \Doctrine\DBAL\Types\StringType, 'type' => Type::getType('string'),
'length' => 255 'length' => 255
) )
); );
......
...@@ -23,4 +23,20 @@ class ForumUser ...@@ -23,4 +23,20 @@ class ForumUser
* @DoctrineJoinColumn(name="avatar_id", referencedColumnName="id") * @DoctrineJoinColumn(name="avatar_id", referencedColumnName="id")
*/ */
public $avatar; public $avatar;
public function getId() {
return $this->id;
}
public function getUsername() {
return $this->username;
}
public function getAvatar() {
return $this->avatar;
}
public function setAvatar(CmsAvatar $avatar) {
$this->avatar = $avatar;
}
} }
\ No newline at end of file
...@@ -150,8 +150,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -150,8 +150,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testManyToManyCollectionClearing() public function testManyToManyCollectionClearing()
{ {
echo PHP_EOL . "MANY-MANY" . PHP_EOL;
$user = new CmsUser; $user = new CmsUser;
$user->name = 'Guilherme'; $user->name = 'Guilherme';
$user->username = 'gblanco'; $user->username = 'gblanco';
...@@ -176,7 +174,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -176,7 +174,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
//$user->groups->clear(); //$user->groups->clear();
unset($user->groups); unset($user->groups);
echo PHP_EOL . "FINAL FLUSH" . PHP_EOL;
$this->_em->flush(); $this->_em->flush();
// Check that the links in the association table have been deleted // Check that the links in the association table have been deleted
......
...@@ -24,7 +24,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -24,7 +24,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
* *
* MAXIMUM TIME: 3 seconds * MAXIMUM TIME: 3 seconds
*/ */
public function testNewHydrationSimpleQueryArrayHydrationPerformance() public function testSimpleQueryArrayHydrationPerformance()
{ {
$rsm = new ResultSetMapping; $rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
...@@ -69,7 +69,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -69,7 +69,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$this->setMaxRunningTime(3); $this->setMaxRunningTime(3);
$s = microtime(true);
$result = $hydrator->hydrateAll($stmt, $rsm); $result = $hydrator->hydrateAll($stmt, $rsm);
$e = microtime(true);
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
} }
/** /**
...@@ -79,7 +82,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -79,7 +82,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
* *
* MAXIMUM TIME: 4 seconds * MAXIMUM TIME: 4 seconds
*/ */
public function testNewHydrationMixedQueryFetchJoinArrayHydrationPerformance() public function testMixedQueryFetchJoinArrayHydrationPerformance()
{ {
$rsm = new ResultSetMapping; $rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
...@@ -140,7 +143,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -140,7 +143,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$this->setMaxRunningTime(4); $this->setMaxRunningTime(4);
$s = microtime(true);
$result = $hydrator->hydrateAll($stmt, $rsm); $result = $hydrator->hydrateAll($stmt, $rsm);
$e = microtime(true);
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
} }
/** /**
...@@ -193,8 +199,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -193,8 +199,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$this->setMaxRunningTime(5); $this->setMaxRunningTime(5);
$s = microtime(true);
$result = $hydrator->hydrateAll($stmt, $rsm); $result = $hydrator->hydrateAll($stmt, $rsm);
echo count($result); $e = microtime(true);
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
} }
/** /**
...@@ -263,7 +271,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -263,7 +271,10 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$this->setMaxRunningTime(4); $this->setMaxRunningTime(4);
$s = microtime(true);
$result = $hydrator->hydrateAll($stmt, $rsm); $result = $hydrator->hydrateAll($stmt, $rsm);
$e = microtime(true);
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment