diff --git a/lib/Doctrine/ActiveEntity.php b/lib/Doctrine/ActiveEntity.php new file mode 100644 index 000000000..5f0884a30 --- /dev/null +++ b/lib/Doctrine/ActiveEntity.php @@ -0,0 +1,455 @@ +_em->save($this); + } + + /** + * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT + * query, except that if there is already a row in the table with the same + * key field values, the REPLACE query just updates its values instead of + * inserting a new row. + * + * The REPLACE type of query does not make part of the SQL standards. Since + * practically only MySQL and SQLIte implement it natively, this type of + * query isemulated through this method for other DBMS using standard types + * of queries inside a transaction to assure the atomicity of the operation. + * + * @param Doctrine_Connection $conn optional connection parameter + * @throws Doctrine_Connection_Exception if some of the key values was null + * @throws Doctrine_Connection_Exception if there were no key fields + * @throws Doctrine_Connection_Exception if something fails at database level + * @return integer number of rows affected + * @todo ActiveEntity method. + */ + public function replace() + { + return $this->_em->replace( + $this->_class, + $this->getPrepared(), + $this->_id); + } + + /** + * Creates an array representation of the object's data. + * + * @param boolean $deep - Return also the relations + * @return array + * @todo ActiveEntity method. + * @todo Move implementation to EntityManager. + */ + public function toArray($deep = true, $prefixKey = false) + { + $a = array(); + + foreach ($this as $column => $value) { + if ($value === Doctrine_Null::$INSTANCE || is_object($value)) { + $value = null; + } + $a[$column] = $value; + } + + if ($this->_class->getIdentifierType() == Doctrine::IDENTIFIER_AUTOINC) { + $idFieldNames = $this->_class->getIdentifier(); + $idFieldName = $idFieldNames[0]; + + $ids = $this->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; + + $a[$idFieldName] = $id; + } + + if ($deep) { + foreach ($this->_references as $key => $relation) { + if ( ! $relation instanceof Doctrine_Null) { + $a[$key] = $relation->toArray($deep, $prefixKey); + } + } + } + + // [FIX] Prevent mapped Doctrine_Entitys from being displayed fully + foreach ($this->_values as $key => $value) { + if ($value instanceof Doctrine_Entity) { + $a[$key] = $value->toArray($deep, $prefixKey); + } else { + $a[$key] = $value; + } + } + + return $a; + } + + /** + * Merges this Entity with an array of values + * or with another existing instance of. + * + * @param mixed $data Data to merge. Either another instance of this model or an array + * @param bool $deep Bool value for whether or not to merge the data deep + * @return void + * @todo ActiveEntity method. + * @todo Move implementation to EntityManager. + */ + public function merge($data, $deep = true) + { + if ($data instanceof $this) { + $array = $data->toArray($deep); + } else if (is_array($data)) { + $array = $data; + } else { + $array = array(); + } + + return $this->fromArray($array, $deep); + } + + /** + * fromArray + * + * @param string $array + * @param bool $deep Bool value for whether or not to merge the data deep + * @return void + * @todo ActiveEntity method. + * @todo Move implementation to EntityManager. + */ + public function fromArray($array, $deep = true) + { + if (is_array($array)) { + foreach ($array as $key => $value) { + if ($deep && $this->getTable()->hasRelation($key)) { + $this->$key->fromArray($value, $deep); + } else if ($this->getTable()->hasField($key)) { + $this->set($key, $value); + } + } + } + } + + /** + * Synchronizes a Doctrine_Entity and its relations with data from an array + * + * It expects an array representation of a Doctrine_Entity similar to the return + * value of the toArray() method. If the array contains relations it will create + * those that don't exist, update the ones that do, and delete the ones missing + * on the array but available on the Doctrine_Entity + * + * @param array $array representation of a Doctrine_Entity + * @todo ActiveEntity method. + * @todo Move implementation to EntityManager. + */ + public function synchronizeFromArray(array $array) + { + foreach ($array as $key => $value) { + if ($this->getTable()->hasRelation($key)) { + $this->get($key)->synchronizeFromArray($value); + } else if ($this->getTable()->hasColumn($key)) { + $this->set($key, $value); + } + } + // eliminate relationships missing in the $array + foreach ($this->_references as $name => $obj) { + if ( ! isset($array[$name])) { + unset($this->$name); + } + } + } + + /** + * exportTo + * + * @param string $type + * @param string $deep + * @return void + * @todo ActiveEntity method. + */ + public function exportTo($type, $deep = true) + { + if ($type == 'array') { + return $this->toArray($deep); + } else { + return Doctrine_Parser::dump($this->toArray($deep, true), $type); + } + } + + /** + * importFrom + * + * @param string $type + * @param string $data + * @return void + * @author Jonathan H. Wage + * @todo ActiveEntity method. + */ + public function importFrom($type, $data) + { + if ($type == 'array') { + return $this->fromArray($data); + } else { + return $this->fromArray(Doctrine_Parser::load($data, $type)); + } + } + + /** + * Deletes the persistent state of the entity. + * + * @return boolean TRUE on success, FALSE on failure. + * @todo ActiveRecord method. + */ + public function delete() + { + // TODO: Forward to EntityManager. There: registerRemoved() on UnitOfWork + return $this->_em->remove($this); + } + + /** + * Creates a copy of the entity. + * + * @return Doctrine_Entity + * @todo ActiveEntity method. Implementation to EntityManager. + */ + public function copy($deep = true) + { + $data = $this->_data; + + if ($this->_class->getIdentifierType() === Doctrine::IDENTIFIER_AUTOINC) { + $idFieldNames = (array)$this->_class->getIdentifier(); + $id = $idFieldNames[0]; + unset($data[$id]); + } + + $ret = $this->_em->createEntity($this->_entityName, $data); + $modified = array(); + + foreach ($data as $key => $val) { + if ( ! ($val instanceof Doctrine_Null)) { + $ret->_modified[] = $key; + } + } + + if ($deep) { + foreach ($this->_references as $key => $value) { + if ($value instanceof Doctrine_Collection) { + foreach ($value as $record) { + $ret->{$key}[] = $record->copy($deep); + } + } else { + $ret->set($key, $value->copy($deep)); + } + } + } + + return $ret; + } + + /** + * Removes links from this record to given records + * if no ids are given, it removes all links + * + * @param string $alias related component alias + * @param array $ids the identifiers of the related records + * @return Doctrine_Entity this object + * @todo ActiveEntity method. + */ + public function unlink($alias, $ids = array()) + { + $ids = (array) $ids; + + $q = new Doctrine_Query(); + + $rel = $this->getTable()->getRelation($alias); + + if ($rel instanceof Doctrine_Relation_Association) { + $q->delete() + ->from($rel->getAssociationTable()->getComponentName()) + ->where($rel->getLocal() . ' = ?', array_values($this->identifier())); + + if (count($ids) > 0) { + $q->whereIn($rel->getForeign(), $ids); + } + + $q->execute(); + + } else if ($rel instanceof Doctrine_Relation_ForeignKey) { + $q->update($rel->getTable()->getComponentName()) + ->set($rel->getForeign(), '?', array(null)) + ->addWhere($rel->getForeign() . ' = ?', array_values($this->identifier())); + + if (count($ids) > 0) { + $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); + $q->whereIn($relTableIdFieldNames[0], $ids); + } + + $q->execute(); + } + if (isset($this->_references[$alias])) { + foreach ($this->_references[$alias] as $k => $record) { + + if (in_array(current($record->identifier()), $ids)) { + $this->_references[$alias]->remove($k); + } + + } + + $this->_references[$alias]->takeSnapshot(); + } + return $this; + } + + + /** + * Creates links from this record to given records. + * + * @param string $alias related component alias + * @param array $ids the identifiers of the related records + * @return Doctrine_Entity this object + * @todo ActiveEntity method. + */ + public function link($alias, array $ids) + { + if ( ! count($ids)) { + return $this; + } + + $identifier = array_values($this->identifier()); + $identifier = array_shift($identifier); + + $rel = $this->getTable()->getRelation($alias); + + if ($rel instanceof Doctrine_Relation_Association) { + $modelClassName = $rel->getAssociationTable()->getComponentName(); + $localFieldName = $rel->getLocalFieldName(); + $localFieldDef = $rel->getAssociationTable()->getColumnDefinition($localFieldName); + if ($localFieldDef['type'] == 'integer') { + $identifier = (integer) $identifier; + } + $foreignFieldName = $rel->getForeignFieldName(); + $foreignFieldDef = $rel->getAssociationTable()->getColumnDefinition($foreignFieldName); + if ($foreignFieldDef['type'] == 'integer') { + for ($i = 0; $i < count($ids); $i++) { + $ids[$i] = (integer) $ids[$i]; + } + } + foreach ($ids as $id) { + $record = new $modelClassName; + $record[$localFieldName] = $identifier; + $record[$foreignFieldName] = $id; + $record->save(); + } + + } else if ($rel instanceof Doctrine_Relation_ForeignKey) { + + $q = new Doctrine_Query(); + + $q->update($rel->getTable()->getComponentName()) + ->set($rel->getForeign(), '?', array_values($this->identifier())); + + if (count($ids) > 0) { + $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); + $q->whereIn($relTableIdFieldNames[0], $ids); + } + + $q->execute(); + + } else if ($rel instanceof Doctrine_Relation_LocalKey) { + $q = new Doctrine_Query(); + $q->update($this->getTable()->getComponentName()) + ->set($rel->getLocalFieldName(), '?', $ids); + + if (count($ids) > 0) { + $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); + $q->whereIn($relTableIdFieldNames[0], array_values($this->identifier())); + } + + $q->execute(); + + } + + return $this; + } + + /** + * Refresh internal data from the database + * + * @param bool $deep If true, fetch also current relations. Caution: this deletes + * any aggregated values you may have queried beforee + * + * @throws Doctrine_Record_Exception When the refresh operation fails (when the database row + * this record represents does not exist anymore) + * @return boolean + * @todo Implementation to EntityManager. + * @todo Move to ActiveEntity (extends Entity). Implementation to EntityManager. + */ + public function refresh($deep = false) + { + $id = $this->identifier(); + if ( ! is_array($id)) { + $id = array($id); + } + if (empty($id)) { + return false; + } + $id = array_values($id); + + if ($deep) { + $query = $this->_em->createQuery()->from($this->_entityName); + foreach (array_keys($this->_references) as $name) { + $query->leftJoin(get_class($this) . '.' . $name); + } + $query->where(implode(' = ? AND ', $this->_class->getIdentifierColumnNames()) . ' = ?'); + $this->clearRelated(); + $record = $query->fetchOne($id); + } else { + // Use FETCH_ARRAY to avoid clearing object relations + $record = $this->getRepository()->find($this->identifier(), Doctrine::HYDRATE_ARRAY); + if ($record) { + $this->hydrate($record); + } + } + + if ($record === false) { + throw new Doctrine_Record_Exception('Failed to refresh. Record does not exist.'); + } + + $this->_modified = array(); + + $this->_extractIdentifier(); + + $this->_state = Doctrine_Entity::STATE_CLEAN; + + return $this; + } + + /** + * Hydrates this object from given array + * + * @param array $data + * @return boolean + */ + final public function hydrate(array $data) + { + $this->_data = array_merge($this->_data, $data); + $this->_extractIdentifier(); + } +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Configuration.php b/lib/Doctrine/Configuration.php index aff0b48b2..cfc897fdc 100644 --- a/lib/Doctrine/Configuration.php +++ b/lib/Doctrine/Configuration.php @@ -63,13 +63,13 @@ class Doctrine_Configuration /** * Initializes the attributes. + * Changes null default values to references to the Null object to allow + * fast isset() checks instead of array_key_exists(). * * @return void */ private function _initAttributes() { - // Change null default values to references to the Null object to allow - // fast isset() checks instead of array_key_exists(). foreach ($this->_attributes as $key => $value) { if ($value === null) { $this->_attributes[$key] = $this->_nullObject; diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index acfc24c46..7d9281911 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -26,7 +26,7 @@ #use Doctrine::DBAL::Exceptions::ConnectionException; /** - * A thin connection wrapper on top of PDO. + * A thin wrapper on top of the PDO class. * * 1. Event listeners * An easy to use, pluggable eventlistener architecture. Aspects such as @@ -117,9 +117,9 @@ abstract class Doctrine_Connection protected $_quoteIdentifiers; /** - * @var array $serverInfo + * @var array */ - protected $serverInfo = array(); + protected $_serverInfo = array(); /** * The parameters used during creation of the Connection. @@ -154,11 +154,18 @@ abstract class Doctrine_Connection protected $_platform; /** - * Enter description here... + * The transaction object. * * @var Doctrine::DBAL::Transactions::Transaction */ protected $_transaction; + + /** + * The sequence manager. + * + * @var Doctrine::DBAL::Sequencing::SequenceManager + */ + protected $_sequenceManager; /** * Constructor. @@ -339,20 +346,13 @@ abstract class Doctrine_Connection * * @return string * @todo make abstract, implement in subclasses. + * @todo throw different exception? */ protected function _constructPdoDsn() { throw Doctrine_Exception::notImplemented('_constructPdoDsn', get_class($this)); } - /** - * @todo Remove. Breaks encapsulation. - */ - public function incrementQueryCount() - { - $this->_queryCount++; - } - /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same @@ -360,8 +360,8 @@ abstract class Doctrine_Connection * inserting a new row. * * The REPLACE type of query does not make part of the SQL standards. Since - * practically only MySQL and SQLIte implement it natively, this type of - * query isemulated through this method for other DBMS using standard types + * practically only MySQL and SQLite implement it natively, this type of + * query is emulated through this method for other DBMS using standard types * of queries inside a transaction to assure the atomicity of the operation. * * @param string name of the table on which the REPLACE query will @@ -490,7 +490,7 @@ abstract class Doctrine_Connection // column names are specified as array keys $cols = array(); - // the query VALUES will contain either expresions (eg 'NOW()') or ? + // the query VALUES will contain either expressions (eg 'NOW()') or ? $a = array(); foreach ($data as $columnName => $value) { $cols[] = $this->quoteIdentifier($columnName); @@ -502,13 +502,10 @@ abstract class Doctrine_Connection } } - // build the statement $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName) . ' (' . implode(', ', $cols) . ') ' . 'VALUES ('; - $query .= implode(', ', $a) . ')'; - // prepare and execute the statement return $this->exec($query, array_values($data)); } @@ -567,14 +564,15 @@ abstract class Doctrine_Connection // quick fix for the identifiers that contain a dot if (strpos($str, '.')) { $e = explode('.', $str); - return $this->quoteIdentifier($e[0]) . '.' + return $this->quoteIdentifier($e[0]) + . '.' . $this->quoteIdentifier($e[1]); } - - $q = $this->properties['identifier_quoting']; - $str = str_replace($q['end'], $q['escape'] . $q['end'], $str); - return $q['start'] . $str . $q['end']; + $c = $this->_platform->getIdentifierQuoteCharacter(); + $str = str_replace($c, $c . $c, $str); + + return $c . $str . $c; } /** @@ -603,55 +601,16 @@ abstract class Doctrine_Connection } /** - * Quotes given input parameter + * Quotes given input parameter. * - * @param mixed $input parameter to be quoted - * @param string $type - * @return mixed + * @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) { return $this->_pdo->quote($input, $type); } - /** - * quote - * quotes given input parameter - * - * @param mixed $input parameter to be quoted - * @param string $type - * @return mixed - */ - /*public function quote($input, $type = null) - { - if ($type == null) { - $type = gettype($input); - } - switch ($type) { - case 'integer': - case 'enum': - case 'boolean': - case 'double': - case 'float': - case 'bool': - case 'decimal': - case 'int': - return $input; - case 'array': - case 'object': - $input = serialize($input); - case 'date': - case 'time': - case 'timestamp': - case 'string': - case 'char': - case 'varchar': - case 'text': - case 'gzip': - case 'blob': - case 'clob': - return $this->conn->quote($input); - } - }*/ /** * Set the date/time format for the current connection @@ -882,21 +841,14 @@ abstract class Doctrine_Connection * @throws Doctrine_Connection_Exception */ public function rethrowException(Exception $e, $invoker) - { - //$event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR); - //$this->getListener()->preError($event); - + { $name = 'Doctrine_Connection_' . $this->_driverName . '_Exception'; - $exc = new $name($e->getMessage(), (int) $e->getCode()); if ( ! is_array($e->errorInfo)) { $e->errorInfo = array(null, null, null, null); } $exc->processErrorInfo($e->errorInfo); - throw $exc; - - //$this->getListener()->postError($event); } /** @@ -1007,7 +959,7 @@ abstract class Doctrine_Connection */ public function commit($savepoint = null) { - return $this->transaction->commit($savepoint); + return $this->_transaction->commit($savepoint); } /** @@ -1025,7 +977,7 @@ abstract class Doctrine_Connection */ public function rollback($savepoint = null) { - $this->transaction->rollback($savepoint); + $this->_transaction->rollback($savepoint); } /** @@ -1202,31 +1154,18 @@ abstract class Doctrine_Connection return Doctrine_Lib::getConnectionAsString($this); } - /* - * ----------- Mixed methods (need to figure out where they go) --------------- - */ - /** - * retrieves a database connection attribute + * Gets the SequenceManager that can be used to retrieve sequence values + * through this connection. * - * @param integer $attribute - * @return mixed - * @todo Implementation or remove if not needed. Configuration is the main - * container for all the attributes now. + * @return Doctrine::DBAL::Sequencing::SequenceManager */ - public function getAttribute($attribute) - { - if ($attribute == Doctrine::ATTR_QUOTE_IDENTIFIER) { - return false; - } - } - public function getSequenceManager() { - if ( ! $this->modules['sequence']) { + if ( ! $this->_sequenceManager) { $class = "Doctrine_Sequence_" . $this->_driverName; - $this->modules['sequence'] = new $class; + $this->_sequenceManager = new $class; } - return $this->modules['sequence']; + return $this->_sequenceManager; } } diff --git a/lib/Doctrine/Connection/Mock.php b/lib/Doctrine/Connection/Mock.php index 8cb18cb98..73313a7b1 100644 --- a/lib/Doctrine/Connection/Mock.php +++ b/lib/Doctrine/Connection/Mock.php @@ -58,7 +58,10 @@ class Doctrine_Connection_Mock extends Doctrine_Connection_Common } public function quote($input, $type = null) - { + { + if ($type === 'string') { + return "'" . $input . "'"; + } return $input; } } \ No newline at end of file diff --git a/lib/Doctrine/Connection/Statement.php b/lib/Doctrine/Connection/Statement.php index b1952bdaa..29604bf9f 100644 --- a/lib/Doctrine/Connection/Statement.php +++ b/lib/Doctrine/Connection/Statement.php @@ -18,12 +18,10 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Adapter_Statement_Interface'); + /** - * Doctrine_Connection_Statement + * A thin wrapper around PDOStatement. * - * @package Doctrine - * @subpackage Connection * @author Konsta Vesterinen * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org @@ -31,7 +29,7 @@ Doctrine::autoload('Doctrine_Adapter_Statement_Interface'); * @version $Revision: 1532 $ * @todo Do we seriously need this wrapper? */ -class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interface +class Doctrine_Connection_Statement { /** * @var Doctrine_Connection $conn Doctrine_Connection object, every connection @@ -40,7 +38,7 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf protected $_conn; /** - * @var mixed $_stmt PDOStatement object, boolean false or Doctrine_Adapter_Statement object + * @var PDOStatement $_stmt PDOStatement object, boolean false or Doctrine_Adapter_Statement object */ protected $_stmt; @@ -81,7 +79,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * bindColumn * Bind a column to a PHP variable * * @param mixed $column Number of the column (1-indexed) or name of the column in the result set. @@ -125,7 +122,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * bindParam * Binds a PHP variable to a corresponding named or question mark placeholder in the * SQL statement that was use to prepare the statement. Unlike Doctrine_Adapter_Statement_Interface->bindValue(), * the variable is bound as a reference and will only be evaluated at the time @@ -161,7 +157,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * closeCursor * Closes the cursor, enabling the statement to be executed again. * * @return boolean Returns TRUE on success or FALSE on failure. @@ -172,7 +167,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * columnCount * Returns the number of columns in the result set * * @return integer Returns the number of columns in the result set represented @@ -185,7 +179,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * errorCode * Fetch the SQLSTATE associated with the last operation on the statement handle * * @see Doctrine_Adapter_Interface::errorCode() @@ -197,7 +190,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * errorInfo * Fetch extended error information associated with the last operation on the statement handle * * @see Doctrine_Adapter_Interface::errorInfo() @@ -209,7 +201,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * execute * Executes a prepared statement * * If the prepared statement included parameter markers, you must either: @@ -226,24 +217,22 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf public function execute($params = null) { try { - $event = new Doctrine_Event($this, Doctrine_Event::STMT_EXECUTE, $this->getQuery(), $params); - $this->_conn->getListener()->preStmtExecute($event); + //$event = new Doctrine_Event($this, Doctrine_Event::STMT_EXECUTE, $this->getQuery(), $params); + //$this->_conn->getListener()->preStmtExecute($event); $result = true; - if ( ! $event->skipOperation) { + //if ( ! $event->skipOperation) { $result = $this->_stmt->execute($params); - $this->_conn->incrementQueryCount(); - } + //$this->_conn->incrementQueryCount(); + //} - $this->_conn->getListener()->postStmtExecute($event); + //$this->_conn->getListener()->postStmtExecute($event); return $result; - } catch (PDOException $e) { - } catch (Doctrine_Adapter_Exception $e) { + } catch (PDOException $e) { + $this->_conn->rethrowException($e, $this); } - $this->_conn->rethrowException($e, $this); - return false; } @@ -278,25 +267,23 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf $cursorOrientation = Doctrine::FETCH_ORI_NEXT, $cursorOffset = null) { - $event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCH, $this->getQuery()); + //$event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCH, $this->getQuery()); + //$event->fetchMode = $fetchMode; + //$event->cursorOrientation = $cursorOrientation; + //$event->cursorOffset = $cursorOffset; - $event->fetchMode = $fetchMode; - $event->cursorOrientation = $cursorOrientation; - $event->cursorOffset = $cursorOffset; + //$data = $this->_conn->getListener()->preFetch($event); - $data = $this->_conn->getListener()->preFetch($event); - - if ( ! $event->skipOperation) { + //if ( ! $event->skipOperation) { $data = $this->_stmt->fetch($fetchMode, $cursorOrientation, $cursorOffset); - } + //} - $this->_conn->getListener()->postFetch($event); + //$this->_conn->getListener()->postFetch($event); return $data; } /** - * fetchAll * Returns an array containing all of the result set rows * * @param integer $fetchMode Controls how the next row will be returned to the caller. @@ -308,32 +295,28 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf * * @return array */ - public function fetchAll($fetchMode = Doctrine::FETCH_BOTH, - $columnIndex = null) + public function fetchAll($fetchMode = Doctrine::FETCH_BOTH, $columnIndex = null) { - $event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCHALL, $this->getQuery()); - $event->fetchMode = $fetchMode; - $event->columnIndex = $columnIndex; + //$event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCHALL, $this->getQuery()); + //$event->fetchMode = $fetchMode; + //$event->columnIndex = $columnIndex; + //$this->_conn->getListener()->preFetchAll($event); - $this->_conn->getListener()->preFetchAll($event); - - if ( ! $event->skipOperation) { + //if ( ! $event->skipOperation) { if ($columnIndex !== null) { $data = $this->_stmt->fetchAll($fetchMode, $columnIndex); } else { $data = $this->_stmt->fetchAll($fetchMode); } + //$event->data = $data; + //} - $event->data = $data; - } - - $this->_conn->getListener()->postFetchAll($event); + //$this->_conn->getListener()->postFetchAll($event); return $data; } /** - * fetchColumn * Returns a single column from the next row of a * result set or FALSE if there are no more rows. * @@ -349,7 +332,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * fetchObject * Fetches the next row and returns it as an object. * * Fetches the next row and returns it as an object. This function is an alternative to @@ -367,7 +349,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * getAttribute * Retrieve a statement attribute * * @param integer $attribute @@ -380,7 +361,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * getColumnMeta * Returns metadata for a column in a result set * * @param integer $column The 0-indexed column in the result set. @@ -401,7 +381,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * nextRowset * Advances to the next rowset in a multi-rowset statement handle * * Some database servers support stored procedures that return more than one rowset @@ -417,7 +396,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * rowCount * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement * executed by the corresponding object. * @@ -434,7 +412,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * setAttribute * Set a statement attribute * * @param integer $attribute @@ -447,7 +424,6 @@ class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interf } /** - * setFetchMode * Set the default fetch mode for this statement * * @param integer $mode The fetch mode must be one of the Doctrine::FETCH_* constants. diff --git a/lib/Doctrine/DatabasePlatform.php b/lib/Doctrine/DatabasePlatform.php index b32329b0a..babf50705 100644 --- a/lib/Doctrine/DatabasePlatform.php +++ b/lib/Doctrine/DatabasePlatform.php @@ -10,66 +10,64 @@ * @author Roman Borschel */ abstract class Doctrine_DatabasePlatform -{ +{ /** - * An array containing all features this platform supports, keys representing feature - * names and values as one of the following (true, false, 'emulated'). - * - * @var array + * Constructor. */ - protected $_supported = array(); - - /** - * Platform specific properties. Subclasses can override these in the - * constructor. - * - * @var array $properties - */ - protected $_properties = array( - 'sql_comments' => array( - array( - 'start' => '--', - 'end' => "\n", - 'escape' => false - ), - array( - 'start' => '/*', - 'end' => '*/', - 'escape' => false - ) - ), - 'identifier_quoting' => array( - 'start' => '"', - 'end' => '"', - 'escape' => '"' - ), - 'string_quoting' => array( - 'start' => "'", - 'end' => "'", - 'escape' => false, - 'escape_pattern' => false - ), - 'wildcards' => array('%', '_'), - 'varchar_max_length' => 255, - ); - public function __construct() {} /** - * Checks whether a certain feature is supported. + * Gets the character used for identifier quoting. * - * @param string $feature the name of the feature - * @return boolean whether or not this drivers supports given feature + * @return string */ - public function supports($feature) + public function getIdentifierQuoteCharacter() { - return (isset($this->_supported[$feature]) && - ($this->_supported[$feature] === 'emulated' || $this->_supported[$feature])); + return '"'; } /** - * regexp - * returns the regular expression operator + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentStartString() + { + return "--"; + } + + /** + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentEndString() + { + return "\n"; + } + + /** + * Gets the maximum length of a varchar field. + * + * @return integer + */ + public function getVarcharMaxLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. * * @return string */ @@ -889,9 +887,10 @@ abstract class Doctrine_DatabasePlatform * * @param string $query The SQL string to write to / append to. * @return string + * @todo Remove the ORM dependency */ - public function writeLimitClauseInSubquery(Doctrine_ClassMetadata $rootClass, $query, - $limit = false, $offset = false) + public function writeLimitClauseInSubquery(Doctrine_ClassMetadata $rootClass, + $query, $limit = false, $offset = false) { return $this->modifyLimitQuery($query, $limit, $offset); } @@ -910,6 +909,46 @@ abstract class Doctrine_DatabasePlatform } return $this->_properties[$name]; } + + public function supportsSequences() + { + return false; + } + + public function supportsIdentityColumns() + { + return false; + } + + public function supportsIndexes() + { + return true; + } + + public function supportsTransactions() + { + return true; + } + + public function supportsSavepoints() + { + return true; + } + + public function supportsPrimaryConstraints() + { + return true; + } + + public function supportsForeignKeyConstraints() + { + return true; + } + + public function supportsGettingAffectedRows() + { + return true; + } } diff --git a/lib/Doctrine/DatabasePlatform/FirebirdPlatform.php b/lib/Doctrine/DatabasePlatform/FirebirdPlatform.php index e6faf1d36..35a1f97e5 100644 --- a/lib/Doctrine/DatabasePlatform/FirebirdPlatform.php +++ b/lib/Doctrine/DatabasePlatform/FirebirdPlatform.php @@ -16,26 +16,6 @@ class Doctrine_DatabasePlatform_FirebirdPlatform extends Doctrine_DatabasePlatfo public function __construct() { parent::__construct(); - $this->_supported = array( - 'sequences' => true, - 'indexes' => true, - 'affected_rows' => true, - 'summary_functions' => true, - 'order_by_text' => true, - 'transactions' => true, - 'savepoints' => true, - 'current_id' => true, - 'limit_queries' => 'emulated', - 'LOBs' => true, - 'replace' => 'emulated', - 'sub_selects' => true, - 'auto_increment' => true, - 'primary_key' => true, - 'result_introspection' => true, - 'prepared_statements' => true, - 'identifier_quoting' => false, - 'pattern_escaping' => true - ); } /** diff --git a/lib/Doctrine/DatabasePlatform/MsSqlPlatform.php b/lib/Doctrine/DatabasePlatform/MsSqlPlatform.php index 697fcff1e..962033c57 100644 --- a/lib/Doctrine/DatabasePlatform/MsSqlPlatform.php +++ b/lib/Doctrine/DatabasePlatform/MsSqlPlatform.php @@ -8,24 +8,6 @@ class Doctrine_DatabasePlatform_MsSqlPlatform extends Doctrine_DatabasePlatform public function __construct() { parent::__construct(); - // initialize all driver options - $this->_supported = array( - 'sequences' => 'emulated', - 'indexes' => true, - 'affected_rows' => true, - 'transactions' => true, - 'summary_functions' => true, - 'order_by_text' => true, - 'current_id' => 'emulated', - 'limit_queries' => 'emulated', - 'LOBs' => true, - 'replace' => 'emulated', - 'sub_selects' => true, - 'auto_increment' => true, - 'primary_key' => true, - 'result_introspection' => true, - 'prepared_statements' => 'emulated', - ); } /** diff --git a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php index a1a8ca428..57694754a 100644 --- a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php +++ b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php @@ -15,7 +15,7 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform * @var array * @todo Needed? What about lazy initialization? */ - protected static $_reservedKeywords = array( + /*protected static $_reservedKeywords = array( 'ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC', 'ASENSITIVE', 'BEFORE', @@ -90,56 +90,30 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform 'WHEN', 'WHERE', 'WHILE', 'WITH', 'WRITE', 'X509', 'XOR', 'YEAR_MONTH', 'ZEROFILL' - ); - + );*/ /** * Constructor. - * Creates a new MySqlPlatform. + * Creates a new MySqlPlatform instance. */ public function __construct() { - parent::__construct(); - $this->_supported = array( - 'sequences' => 'emulated', - 'indexes' => true, - 'affected_rows' => true, - 'transactions' => true, - 'savepoints' => false, - 'summary_functions' => true, - 'order_by_text' => true, - 'current_id' => 'emulated', - 'limit_queries' => true, - 'LOBs' => true, - 'replace' => true, - 'sub_selects' => true, - 'auto_increment' => true, - 'primary_key' => true, - 'result_introspection' => true, - 'prepared_statements' => 'emulated', - 'identifier_quoting' => true, - 'pattern_escaping' => true - ); - $this->_properties['string_quoting'] = array( - 'start' => "'", - 'end' => "'", - 'escape' => '\\', - 'escape_pattern' => '\\'); - $this->_properties['identifier_quoting'] = array( - 'start' => '`', - 'end' => '`', - 'escape' => '`'); - $this->_properties['sql_comments'] = array( - array('start' => '-- ', 'end' => "\n", 'escape' => false), - array('start' => '#', 'end' => "\n", 'escape' => false), - array('start' => '/*', 'end' => '*/', 'escape' => false), - ); - - $this->properties['varchar_max_length'] = 255; + parent::__construct(); } /** - * returns the regular expression operator + * Gets the character used for identifier quoting. + * + * @return string + * @override + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * Returns the regular expression operator. * * @return string * @override @@ -204,6 +178,7 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform } $match.= "'"; $match.= $this->patternEscapeString(); + return $match; } @@ -555,6 +530,29 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform { return true; } + + /** + * Whether the platform supports identity columns. + * MySql supports this through AUTO_INCREMENT columns. + * + * @return boolean + * @override + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * Whether the platform supports savepoints. MySql does not. + * + * @return boolean + * @override + */ + public function supportsSavepoints() + { + return false; + } } ?> \ No newline at end of file diff --git a/lib/Doctrine/DatabasePlatform/OraclePlatform.php b/lib/Doctrine/DatabasePlatform/OraclePlatform.php index a1011887e..3f7cec6bc 100644 --- a/lib/Doctrine/DatabasePlatform/OraclePlatform.php +++ b/lib/Doctrine/DatabasePlatform/OraclePlatform.php @@ -8,26 +8,6 @@ class Doctrine_DatabasePlatform_OraclePlatform extends Doctrine_DatabasePlatform public function __construct() { parent::__construct(); - $this->_supported = array( - 'sequences' => true, - 'indexes' => true, - 'summary_functions' => true, - 'order_by_text' => true, - 'current_id' => true, - 'affected_rows' => true, - 'transactions' => true, - 'savepoints' => true, - 'limit_queries' => true, - 'LOBs' => true, - 'replace' => 'emulated', - 'sub_selects' => true, - 'auto_increment' => false, // implementation is broken - 'primary_key' => true, - 'result_introspection' => true, - 'prepared_statements' => true, - 'identifier_quoting' => true, - 'pattern_escaping' => true, - ); } /** @@ -44,6 +24,9 @@ class Doctrine_DatabasePlatform_OraclePlatform extends Doctrine_DatabasePlatform return $this->_createLimitSubquery($query, $limit, $offset); } + /** + * @todo Doc + */ private function _createLimitSubquery($query, $limit, $offset, $column = null) { $limit = (int) $limit; diff --git a/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php b/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php index a3f786e1f..75b30ed88 100644 --- a/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php +++ b/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php @@ -9,7 +9,7 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat * @param array * @todo Nedded? What about lazy initialization? */ - protected static $_reservedKeywords = array( + /*protected static $_reservedKeywords = array( 'abort', 'absolute', 'access', 'action', 'add', 'after', 'aggregate', 'all', 'alter', 'analyse', 'analyze', 'and', 'any', 'as', 'asc', 'assertion', 'assignment', 'at', 'authorization', 'backward', 'before', @@ -54,7 +54,7 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat 'unknown', 'unlisten', 'until', 'update', 'usage', 'user', 'using', 'vacuum', 'valid', 'validator', 'values', 'varchar', 'varying', 'verbose', 'version', 'view', 'volatile', 'when', 'where', 'with', - 'without', 'work', 'write', 'year','zone'); + 'without', 'work', 'write', 'year','zone');*/ /** @@ -63,28 +63,7 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat */ public function __construct() { - parent::__construct(); - $this->_supported = array( - 'sequences' => true, - 'indexes' => true, - 'affected_rows' => true, - 'summary_functions' => true, - 'order_by_text' => true, - 'transactions' => true, - 'savepoints' => true, - 'current_id' => true, - 'limit_queries' => true, - 'LOBs' => true, - 'replace' => 'emulated', - 'sub_selects' => true, - 'auto_increment' => 'emulated', - 'primary_key' => true, - 'result_introspection' => true, - 'prepared_statements' => true, - 'identifier_quoting' => true, - 'pattern_escaping' => true, - ); - + parent::__construct(); $this->_properties['string_quoting'] = array('start' => "'", 'end' => "'", 'escape' => "'", @@ -94,7 +73,6 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat 'escape' => '"'); } - /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. @@ -509,7 +487,39 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat public function parseBoolean($value) { return $value; - } + } + + /** + * Whether the platform supports sequences. + * Postgres has native support for sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return true; + } + + /** + * Whether the platform supports identity columns. + * Postgres supports these through the SERIAL keyword. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * Whether the platform prefers sequences for ID generation. + * + * @return boolean + */ + public function prefersSequences() + { + return true; + } } ?> \ No newline at end of file diff --git a/lib/Doctrine/DatabasePlatform/SqlitePlatform.php b/lib/Doctrine/DatabasePlatform/SqlitePlatform.php index 15673544d..0048d1143 100644 --- a/lib/Doctrine/DatabasePlatform/SqlitePlatform.php +++ b/lib/Doctrine/DatabasePlatform/SqlitePlatform.php @@ -16,26 +16,6 @@ class Doctrine_DatabasePlatform_SqlitePlatform extends Doctrine_DatabasePlatform public function __construct() { parent::__construct(); - $this->_supported = array( - 'sequences' => 'emulated', - 'indexes' => true, - 'affected_rows' => true, - 'summary_functions' => true, - 'order_by_text' => true, - 'current_id' => 'emulated', - 'limit_queries' => true, - 'LOBs' => true, - 'replace' => true, - 'transactions' => true, - 'savepoints' => false, - 'sub_selects' => true, - 'auto_increment' => true, - 'primary_key' => true, - 'result_introspection' => false, // not implemented - 'prepared_statements' => 'emulated', - 'identifier_quoting' => true, - 'pattern_escaping' => false, - ); } /** diff --git a/lib/Doctrine/Entity.php b/lib/Doctrine/Entity.php index e92059314..2deb32c7a 100644 --- a/lib/Doctrine/Entity.php +++ b/lib/Doctrine/Entity.php @@ -39,7 +39,6 @@ * @link www.phpdoctrine.org * @since 2.0 * @version $Revision: 4342 $ - * @todo Split up into "Entity" and "ActiveEntity" (extends Entity). */ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable { @@ -208,18 +207,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable return $this->_oid; } - /** - * Hydrates this object from given array - * - * @param array $data - * @return boolean - */ - final public function hydrate(array $data) - { - $this->_data = array_merge($this->_data, $data); - $this->_extractIdentifier(); - } - /** * Copies the identifier names and values from _data into _id. */ @@ -375,58 +362,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } } - /** - * Refresh internal data from the database - * - * @param bool $deep If true, fetch also current relations. Caution: this deletes - * any aggregated values you may have queried beforee - * - * @throws Doctrine_Record_Exception When the refresh operation fails (when the database row - * this record represents does not exist anymore) - * @return boolean - * @todo Implementation to EntityManager. - * @todo Move to ActiveEntity (extends Entity). Implementation to EntityManager. - */ - public function refresh($deep = false) - { - $id = $this->identifier(); - if ( ! is_array($id)) { - $id = array($id); - } - if (empty($id)) { - return false; - } - $id = array_values($id); - - if ($deep) { - $query = $this->_em->createQuery()->from($this->_entityName); - foreach (array_keys($this->_references) as $name) { - $query->leftJoin(get_class($this) . '.' . $name); - } - $query->where(implode(' = ? AND ', $this->_class->getIdentifierColumnNames()) . ' = ?'); - $this->clearRelated(); - $record = $query->fetchOne($id); - } else { - // Use FETCH_ARRAY to avoid clearing object relations - $record = $this->getRepository()->find($this->identifier(), Doctrine::HYDRATE_ARRAY); - if ($record) { - $this->hydrate($record); - } - } - - if ($record === false) { - throw new Doctrine_Record_Exception('Failed to refresh. Record does not exist.'); - } - - $this->_modified = array(); - - $this->_extractIdentifier(); - - $this->_state = Doctrine_Entity::STATE_CLEAN; - - return $this; - } - /** * Gets the current field values. * @@ -438,48 +373,38 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } /** - * INTERNAL: (Usage from within extending classes is intended) - * * Gets the value of a field (regular field or reference). * If the field is not yet loaded this method does NOT load it. - * - * NOTE: Use of this method from outside the scope of an extending class - * is strongly discouraged. * * @param $name name of the property * @throws Doctrine_Entity_Exception if trying to get an unknown field * @return mixed */ - final public function _get($fieldName) + final protected function _get($fieldName) { if (isset($this->_data[$fieldName])) { - return $this->_rawGetField($fieldName); + return $this->_internalGetField($fieldName); } else if (isset($this->_references[$fieldName])) { - return $this->_rawGetReference($fieldName); + return $this->_internalGetReference($fieldName); } else { throw Doctrine_Entity_Exception::unknownField($fieldName); } } /** - * INTERNAL: (Usage from within extending classes is intended) - * * Sets the value of a field (regular field or reference). * If the field is not yet loaded this method does NOT load it. - * - * NOTE: Use of this method from outside the scope of an extending class - * is strongly discouraged. * * @param $name name of the field * @throws Doctrine_Entity_Exception if trying to get an unknown field * @return mixed */ - final public function _set($fieldName, $value) + final protected function _set($fieldName, $value) { - if (isset($this->_data[$fieldName])) { - return $this->_rawSetField($fieldName, $value); - } else if (isset($this->_references[$fieldName])) { - return $this->_rawSetReference($fieldName, $value); + if ($this->_class->hasField($fieldName)) { + return $this->_internalSetField($fieldName, $value); + } else if ($this->_class->hasRelation($fieldName)) { + return $this->_internalSetReference($fieldName, $value); } else { throw Doctrine_Entity_Exception::unknownField($fieldName); } @@ -489,15 +414,15 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable * INTERNAL: * Gets the value of a field. * - * NOTE: Use of this method from outside the scope of an extending class - * is strongly discouraged. This method does NOT check whether the field - * exists. _get() in extending classes should be preferred. + * NOTE: Use of this method in userland code is strongly discouraged. + * This method does NOT check whether the field exists. + * _get() in extending classes should be preferred. * * @param string $fieldName * @return mixed * @todo Rename to _unsafeGetField() */ - final public function _rawGetField($fieldName) + final public function _internalGetField($fieldName) { if ($this->_data[$fieldName] === Doctrine_Null::$INSTANCE) { return null; @@ -509,15 +434,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable * INTERNAL: * Sets the value of a field. * - * NOTE: Use of this method from outside the scope of an extending class - * is strongly discouraged. This method does NOT check whether the field - * exists. _set() in extending classes should be preferred. + * NOTE: Use of this method in userland code is strongly discouraged. + * This method does NOT check whether the field exists. + * _set() in extending classes should be preferred. * * @param string $fieldName * @param mixed $value - * @todo Rename to _unsafeSetField() */ - final public function _rawSetField($fieldName, $value) + final public function _internalSetField($fieldName, $value) { $this->_data[$fieldName] = $value; } @@ -525,14 +449,12 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable /** * Gets a reference to another Entity. * - * NOTE: Use of this method from outside the scope of an extending class - * is strongly discouraged. This method does NOT check whether the reference - * exists. + * NOTE: Use of this method in userland code is strongly discouraged. + * This method does NOT check whether the reference exists. * * @param string $fieldName - * @todo Rename to _unsafeGetReference(). */ - final public function _rawGetReference($fieldName) + final public function _internalGetReference($fieldName) { if ($this->_references[$fieldName] === Doctrine_Null::$INSTANCE) { return null; @@ -544,14 +466,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable * INTERNAL: * Sets a reference to another Entity. * - * NOTE: Use of this method is strongly discouraged for user-code. + * NOTE: Use of this method in userland code is strongly discouraged. * * @param string $fieldName * @param mixed $value * @todo Refactor. What about composite keys? * @todo Rename to _unsafeSetReference() */ - final public function _rawSetReference($name, $value) + final public function _internalSetReference($name, $value) { if ($value === Doctrine_Null::$INSTANCE) { $this->_references[$name] = $value; @@ -602,11 +524,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } /** - * Generic getter. + * Generic getter for all persistent fields. * - * @param mixed $name name of the property or related component - * @param boolean $load whether or not to invoke the loading procedure - * @throws Doctrine_Record_Exception if trying to get a value of unknown property / related component + * @param string $fieldName Name of the field. * @return mixed */ final public function get($fieldName) @@ -710,10 +630,10 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } /** - * Generic setter. + * Generic setter for persistent fields. * - * @param mixed $name name of the property or reference - * @param mixed $value value of the property or reference + * @param string $name The name of the field to set. + * @param mixed $value The value of the field. */ final public function set($fieldName, $value) { @@ -742,7 +662,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } } } else if ($this->_class->hasRelation($fieldName)) { - $this->_rawSetReference($fieldName, $value); + $this->_internalSetReference($fieldName, $value); } else { throw Doctrine_Entity_Exception::invalidField($fieldName); } @@ -795,45 +715,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable } } } - - /** - * Saves the current state of the entity into the database. - * - * @param Doctrine_Connection $conn optional connection parameter - * @return void - * @todo ActiveEntity method. - */ - public function save() - { - // TODO: Forward to EntityManager. There: registerNew() OR registerDirty() on UnitOfWork. - $this->_em->save($this); - } - - /** - * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT - * query, except that if there is already a row in the table with the same - * key field values, the REPLACE query just updates its values instead of - * inserting a new row. - * - * The REPLACE type of query does not make part of the SQL standards. Since - * practically only MySQL and SQLIte implement it natively, this type of - * query isemulated through this method for other DBMS using standard types - * of queries inside a transaction to assure the atomicity of the operation. - * - * @param Doctrine_Connection $conn optional connection parameter - * @throws Doctrine_Connection_Exception if some of the key values was null - * @throws Doctrine_Connection_Exception if there were no key fields - * @throws Doctrine_Connection_Exception if something fails at database level - * @return integer number of rows affected - * @todo ActiveEntity method. - */ - public function replace() - { - return $this->_em->replace( - $this->_class, - $this->getPrepared(), - $this->_id); - } /** * INTERNAL: @@ -908,160 +789,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable return $dataSet; } - - /** - * Creates an array representation of the object's data. - * - * @param boolean $deep - Return also the relations - * @return array - * @todo ActiveEntity method. - */ - public function toArray($deep = true, $prefixKey = false) - { - $a = array(); - - foreach ($this as $column => $value) { - if ($value === Doctrine_Null::$INSTANCE || is_object($value)) { - $value = null; - } - $a[$column] = $value; - } - - if ($this->_class->getIdentifierType() == Doctrine::IDENTIFIER_AUTOINC) { - $idFieldNames = $this->_class->getIdentifier(); - $idFieldName = $idFieldNames[0]; - - $ids = $this->identifier(); - $id = count($ids) > 0 ? array_pop($ids) : null; - - $a[$idFieldName] = $id; - } - - if ($deep) { - foreach ($this->_references as $key => $relation) { - if ( ! $relation instanceof Doctrine_Null) { - $a[$key] = $relation->toArray($deep, $prefixKey); - } - } - } - - // [FIX] Prevent mapped Doctrine_Entitys from being displayed fully - foreach ($this->_values as $key => $value) { - if ($value instanceof Doctrine_Entity) { - $a[$key] = $value->toArray($deep, $prefixKey); - } else { - $a[$key] = $value; - } - } - - return $a; - } - - /** - * Merges this Entity with an array of values - * or with another existing instance of. - * - * @param mixed $data Data to merge. Either another instance of this model or an array - * @param bool $deep Bool value for whether or not to merge the data deep - * @return void - * @todo ActiveEntity method. - */ - public function merge($data, $deep = true) - { - if ($data instanceof $this) { - $array = $data->toArray($deep); - } else if (is_array($data)) { - $array = $data; - } else { - $array = array(); - } - - return $this->fromArray($array, $deep); - } - - /** - * fromArray - * - * @param string $array - * @param bool $deep Bool value for whether or not to merge the data deep - * @return void - * @todo ActiveEntity method. - */ - public function fromArray($array, $deep = true) - { - if (is_array($array)) { - foreach ($array as $key => $value) { - if ($deep && $this->getTable()->hasRelation($key)) { - $this->$key->fromArray($value, $deep); - } else if ($this->getTable()->hasField($key)) { - $this->set($key, $value); - } - } - } - } - - /** - * Synchronizes a Doctrine_Entity and its relations with data from an array - * - * It expects an array representation of a Doctrine_Entity similar to the return - * value of the toArray() method. If the array contains relations it will create - * those that don't exist, update the ones that do, and delete the ones missing - * on the array but available on the Doctrine_Entity - * - * @param array $array representation of a Doctrine_Entity - * @todo ActiveEntity method. - */ - public function synchronizeFromArray(array $array) - { - foreach ($array as $key => $value) { - if ($this->getTable()->hasRelation($key)) { - $this->get($key)->synchronizeFromArray($value); - } else if ($this->getTable()->hasColumn($key)) { - $this->set($key, $value); - } - } - // eliminate relationships missing in the $array - foreach ($this->_references as $name => $obj) { - if ( ! isset($array[$name])) { - unset($this->$name); - } - } - } - - /** - * exportTo - * - * @param string $type - * @param string $deep - * @return void - * @todo ActiveEntity method. - */ - public function exportTo($type, $deep = true) - { - if ($type == 'array') { - return $this->toArray($deep); - } else { - return Doctrine_Parser::dump($this->toArray($deep, true), $type); - } - } - - /** - * importFrom - * - * @param string $type - * @param string $data - * @return void - * @author Jonathan H. Wage - * @todo ActiveEntity method. - */ - public function importFrom($type, $data) - { - if ($type == 'array') { - return $this->fromArray($data); - } else { - return $this->fromArray(Doctrine_Parser::load($data, $type)); - } - } /** * Checks whether the entity already has a persistent state. @@ -1084,58 +811,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable return count($this->_modified) > 0; } - /** - * Deletes the persistent state of the entity. - * - * @return boolean TRUE on success, FALSE on failure. - * @todo ActiveRecord method. - */ - public function delete() - { - // TODO: Forward to EntityManager. There: registerRemoved() on UnitOfWork - return $this->_em->remove($this); - } - - /** - * Creates a copy of the entity. - * - * @return Doctrine_Entity - * @todo ActiveEntity method. Implementation to EntityManager. - */ - public function copy($deep = true) - { - $data = $this->_data; - - if ($this->_class->getIdentifierType() === Doctrine::IDENTIFIER_AUTOINC) { - $idFieldNames = (array)$this->_class->getIdentifier(); - $id = $idFieldNames[0]; - unset($data[$id]); - } - - $ret = $this->_em->createEntity($this->_entityName, $data); - $modified = array(); - - foreach ($data as $key => $val) { - if ( ! ($val instanceof Doctrine_Null)) { - $ret->_modified[] = $key; - } - } - - if ($deep) { - foreach ($this->_references as $key => $value) { - if ($value instanceof Doctrine_Collection) { - foreach ($value as $record) { - $ret->{$key}[] = $record->copy($deep); - } - } else { - $ret->set($key, $value->copy($deep)); - } - } - } - - return $ret; - } - /** * INTERNAL: * Assigns an identifier to the entity. This is only intended for use by @@ -1219,132 +894,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable $this->_references[$alias] = $coll; } - /** - * Removes links from this record to given records - * if no ids are given, it removes all links - * - * @param string $alias related component alias - * @param array $ids the identifiers of the related records - * @return Doctrine_Entity this object - * @todo ActiveEntity method. - */ - public function unlink($alias, $ids = array()) - { - $ids = (array) $ids; - - $q = new Doctrine_Query(); - - $rel = $this->getTable()->getRelation($alias); - - if ($rel instanceof Doctrine_Relation_Association) { - $q->delete() - ->from($rel->getAssociationTable()->getComponentName()) - ->where($rel->getLocal() . ' = ?', array_values($this->identifier())); - - if (count($ids) > 0) { - $q->whereIn($rel->getForeign(), $ids); - } - - $q->execute(); - - } else if ($rel instanceof Doctrine_Relation_ForeignKey) { - $q->update($rel->getTable()->getComponentName()) - ->set($rel->getForeign(), '?', array(null)) - ->addWhere($rel->getForeign() . ' = ?', array_values($this->identifier())); - - if (count($ids) > 0) { - $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); - $q->whereIn($relTableIdFieldNames[0], $ids); - } - - $q->execute(); - } - if (isset($this->_references[$alias])) { - foreach ($this->_references[$alias] as $k => $record) { - - if (in_array(current($record->identifier()), $ids)) { - $this->_references[$alias]->remove($k); - } - - } - - $this->_references[$alias]->takeSnapshot(); - } - return $this; - } - - - /** - * Creates links from this record to given records. - * - * @param string $alias related component alias - * @param array $ids the identifiers of the related records - * @return Doctrine_Entity this object - * @todo ActiveEntity method. - */ - public function link($alias, array $ids) - { - if ( ! count($ids)) { - return $this; - } - - $identifier = array_values($this->identifier()); - $identifier = array_shift($identifier); - - $rel = $this->getTable()->getRelation($alias); - - if ($rel instanceof Doctrine_Relation_Association) { - $modelClassName = $rel->getAssociationTable()->getComponentName(); - $localFieldName = $rel->getLocalFieldName(); - $localFieldDef = $rel->getAssociationTable()->getColumnDefinition($localFieldName); - if ($localFieldDef['type'] == 'integer') { - $identifier = (integer) $identifier; - } - $foreignFieldName = $rel->getForeignFieldName(); - $foreignFieldDef = $rel->getAssociationTable()->getColumnDefinition($foreignFieldName); - if ($foreignFieldDef['type'] == 'integer') { - for ($i = 0; $i < count($ids); $i++) { - $ids[$i] = (integer) $ids[$i]; - } - } - foreach ($ids as $id) { - $record = new $modelClassName; - $record[$localFieldName] = $identifier; - $record[$foreignFieldName] = $id; - $record->save(); - } - - } else if ($rel instanceof Doctrine_Relation_ForeignKey) { - - $q = new Doctrine_Query(); - - $q->update($rel->getTable()->getComponentName()) - ->set($rel->getForeign(), '?', array_values($this->identifier())); - - if (count($ids) > 0) { - $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); - $q->whereIn($relTableIdFieldNames[0], $ids); - } - - $q->execute(); - - } else if ($rel instanceof Doctrine_Relation_LocalKey) { - $q = new Doctrine_Query(); - $q->update($this->getTable()->getComponentName()) - ->set($rel->getLocalFieldName(), '?', $ids); - - if (count($ids) > 0) { - $relTableIdFieldNames = (array)$rel->getTable()->getIdentifier(); - $q->whereIn($relTableIdFieldNames[0], array_values($this->identifier())); - } - - $q->execute(); - - } - - return $this; - } - /** * Gets the ClassMetadata object that describes the entity class. * @@ -1390,7 +939,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable */ public function __toString() { - return (string) $this->_oid; + return (string)$this->_oid; } /** diff --git a/lib/Doctrine/Hydrator/RecordDriver.php b/lib/Doctrine/Hydrator/RecordDriver.php index 420dec8b9..9b425932a 100644 --- a/lib/Doctrine/Hydrator/RecordDriver.php +++ b/lib/Doctrine/Hydrator/RecordDriver.php @@ -74,7 +74,7 @@ class Doctrine_Hydrator_RecordDriver $relatedClass = $relation->getTable(); $coll = $this->getElementCollection($relatedClass->getClassName()); $coll->setReference($entity, $relation); - $entity->_rawSetReference($name, $coll); + $entity->_internalSetReference($name, $coll); $this->_initializedRelations[$entity->getOid()][$name] = true; } } @@ -97,23 +97,23 @@ class Doctrine_Hydrator_RecordDriver public function addRelatedIndexedElement(Doctrine_Entity $entity1, $property, Doctrine_Entity $entity2, $indexField) { - $entity1->_rawGetReference($property)->add($entity2, $entity2->_rawGetField($indexField)); + $entity1->_internalGetReference($property)->add($entity2, $entity2->_internalGetField($indexField)); } public function addRelatedElement(Doctrine_Entity $entity1, $property, Doctrine_Entity $entity2) { - $entity1->_rawGetReference($property)->add($entity2); + $entity1->_internalGetReference($property)->add($entity2); } public function setRelatedElement(Doctrine_Entity $entity1, $property, $entity2) { - $entity1->_rawSetReference($property, $entity2); + $entity1->_internalSetReference($property, $entity2); } public function isIndexKeyInUse(Doctrine_Entity $entity, $assocField, $indexField) { - return $entity->_rawGetReference($assocField)->contains($indexField); + return $entity->_internalGetReference($assocField)->contains($indexField); } public function isFieldSet(Doctrine_Entity $entity, $field) @@ -123,17 +123,17 @@ class Doctrine_Hydrator_RecordDriver public function getFieldValue(Doctrine_Entity $entity, $field) { - return $entity->_rawGetField($field); + return $entity->_internalGetField($field); } public function getReferenceValue(Doctrine_Entity $entity, $field) { - return $entity->_rawGetReference($field); + return $entity->_internalGetReference($field); } public function addElementToIndexedCollection($coll, $entity, $keyField) { - $coll->add($entity, $entity->_rawGetField($keyField)); + $coll->add($entity, $entity->_internalGetField($keyField)); } public function addElementToCollection($coll, $entity) diff --git a/lib/Doctrine/Query/Parser.php b/lib/Doctrine/Query/Parser.php index bdabc4f7b..f53ab3e71 100644 --- a/lib/Doctrine/Query/Parser.php +++ b/lib/Doctrine/Query/Parser.php @@ -165,7 +165,6 @@ class Doctrine_Query_Parser if ( ! $isMatch) { // No definition for value checking. $this->syntaxError($this->_keywordTable->getLiteral($token)); - } $this->next(); diff --git a/lib/Doctrine/Query/Production/Atom.php b/lib/Doctrine/Query/Production/Atom.php index 267c6cf17..ce79e001e 100644 --- a/lib/Doctrine/Query/Production/Atom.php +++ b/lib/Doctrine/Query/Production/Atom.php @@ -45,26 +45,26 @@ class Doctrine_Query_Production_Atom extends Doctrine_Query_Production case Doctrine_Query_Token::T_STRING: $this->_parser->match(Doctrine_Query_Token::T_STRING); $this->_type = 'string'; - break; + break; case Doctrine_Query_Token::T_INTEGER: $this->_parser->match(Doctrine_Query_Token::T_INTEGER); $this->_type = 'integer'; - break; + break; case Doctrine_Query_Token::T_FLOAT: $this->_parser->match(Doctrine_Query_Token::T_FLOAT); $this->_type = 'float'; - break; + break; case Doctrine_Query_Token::T_INPUT_PARAMETER: $this->_parser->match(Doctrine_Query_Token::T_INPUT_PARAMETER); $this->_type = 'param'; - break; + break; default: $this->_parser->syntaxError('string, number or parameter (? or :)'); - break; + break; } $this->_value = $this->_parser->token['value']; @@ -78,19 +78,20 @@ class Doctrine_Query_Production_Atom extends Doctrine_Query_Production switch ($this->_type) { case 'param': return $this->_value; - break; - - case 'integer': - case 'float': + case 'string': + //FIXME: Remove the quotes from _value! Should the scanner do that or where? + // 'mystring' => mystring. Otherwise 'mystring' is the content (with quotes)! + // Eg: select ... from ... where f.foo = 'bar' + // => $conn->quote('bar',...), CURRENTLY: $conn->quote("'bar'",...) + + // This fix looks a bit ugly or ... ? Should this happen earlier? Syntax? + // Scanner? + if (strpos($this->_value, "'") === 0) { + $this->_value = substr($this->_value, 1, strlen($this->_value) - 2); + } return $conn->quote($this->_value, $this->_type); - break; - default: - $stringQuoting = $conn->getDatabasePlatform()->getProperty('string_quoting'); - return $stringQuoting['start'] - . $conn->quote($this->_value, $this->_type) - . $stringQuoting['end']; - break; + return $conn->quote($this->_value, $this->_type); } } diff --git a/lib/Doctrine/Query/Scanner.php b/lib/Doctrine/Query/Scanner.php index 075f65f18..634f0f47c 100644 --- a/lib/Doctrine/Query/Scanner.php +++ b/lib/Doctrine/Query/Scanner.php @@ -33,14 +33,18 @@ class Doctrine_Query_Scanner { /** - * Array of scanned tokens + * Array of scanned tokens. * * @var array */ protected $_tokens = array(); - + /** + * @todo Doc + */ protected $_position = 0; - + /** + * @todo Doc + */ protected $_peek = 0; /** @@ -109,7 +113,9 @@ class Doctrine_Query_Scanner } } - + /** + * @todo Doc + */ protected function _getType(&$value) { // $value is referenced because it can be changed if it is numeric. @@ -117,30 +123,32 @@ class Doctrine_Query_Scanner $type = Doctrine_Query_Token::T_NONE; $newVal = $this->_getNumeric($value); - if($newVal !== false){ + if ($newVal !== false){ $value = $newVal; if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { $type = Doctrine_Query_Token::T_FLOAT; - } else{ + } else { $type = Doctrine_Query_Token::T_INTEGER; } } if ($value[0] === "'" && $value[strlen($value) - 1] === "'") { $type = Doctrine_Query_Token::T_STRING; - } elseif (ctype_alpha($value[0]) || $value[0] === '_') { + } else if (ctype_alpha($value[0]) || $value[0] === '_') { $type = $this->_checkLiteral($value); - } elseif ($value[0] === '?' || $value[0] === ':') { + } else if ($value[0] === '?' || $value[0] === ':') { $type = Doctrine_Query_Token::T_INPUT_PARAMETER; } return $type; } - + /** + * @todo Doc + */ protected function _getNumeric($value) { - if (!is_scalar($value)) { + if ( ! is_scalar($value)) { return false; } // Checking for valid numeric numbers: 1.234, -1.234e-2 @@ -164,7 +172,9 @@ class Doctrine_Query_Scanner } - + /** + * @todo Doc + */ public function isA($value, $token) { $type = $this->_getType($value); @@ -172,7 +182,9 @@ class Doctrine_Query_Scanner return $type === $token; } - + /** + * @todo Doc + */ public function peek() { if (isset($this->_tokens[$this->_position + $this->_peek])) { @@ -182,13 +194,14 @@ class Doctrine_Query_Scanner } } - + /** + * @todo Doc + */ public function resetPeek() { $this->_peek = 0; } - /** * Returns the next token in the input string. * @@ -211,7 +224,9 @@ class Doctrine_Query_Scanner } } - + /** + * @todo Doc + */ public function resetPosition($position = 0) { $this->_position = $position; diff --git a/lib/Doctrine/Relation/Parser.php b/lib/Doctrine/Relation/Parser.php index f3e80b1be..d2bb39364 100644 --- a/lib/Doctrine/Relation/Parser.php +++ b/lib/Doctrine/Relation/Parser.php @@ -29,7 +29,8 @@ * @version $Revision: 1397 $ * @link www.phpdoctrine.org * @since 1.0 - * @todo Composite key support? + * @todo Composite key support? + * @todo Remove. Association mapping needs a reimplementation. */ class Doctrine_Relation_Parser { diff --git a/lib/Doctrine/Sequence/Mysql.php b/lib/Doctrine/Sequence/Mysql.php index 1ac508c4e..aaf7a510f 100644 --- a/lib/Doctrine/Sequence/Mysql.php +++ b/lib/Doctrine/Sequence/Mysql.php @@ -44,9 +44,9 @@ class Doctrine_Sequence_Mysql extends Doctrine_Sequence */ public function nextId($seqName, $onDemand = true) { - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); - $query = 'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (NULL)'; + $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); + $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); + $query = 'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (NULL)'; try { $this->conn->exec($query); @@ -68,7 +68,7 @@ class Doctrine_Sequence_Mysql extends Doctrine_Sequence $query = 'DELETE FROM ' . $sequenceName . ' WHERE ' . $seqcolName . ' < ' . $value; try { $this->conn->exec($query); - } catch(Doctrine_Exception $e) { + } catch (Doctrine_Exception $e) { throw new Doctrine_Sequence_Exception('could not delete previous sequence table values from ' . $seqName); } } diff --git a/lib/Doctrine/Sequence/Oracle.php b/lib/Doctrine/Sequence/Oracle.php index e842de1ca..868560826 100644 --- a/lib/Doctrine/Sequence/Oracle.php +++ b/lib/Doctrine/Sequence/Oracle.php @@ -40,7 +40,7 @@ class Doctrine_Sequence_Oracle extends Doctrine_Sequence * * @return integer next id in the given sequence */ - public function nextID($seqName, $onDemand = true) + public function nextId($seqName, $onDemand = true) { $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); $query = 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; @@ -69,10 +69,10 @@ class Doctrine_Sequence_Oracle extends Doctrine_Sequence * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted */ - public function lastInsertID($table = null, $field = null) + public function lastInsertId($table = null, $field = null) { $seqName = $table . (empty($field) ? '' : '_'.$field); - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); + $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); return $this->conn->fetchOne('SELECT ' . $sequenceName . '.currval'); } @@ -84,7 +84,7 @@ class Doctrine_Sequence_Oracle extends Doctrine_Sequence * * @return integer current id in the given sequence */ - public function currID($seqName) + public function currId($seqName) { $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); $query = 'SELECT (last_number-1) FROM user_sequences'; diff --git a/tests/Orm/Entity/AccessorTest.php b/tests/Orm/Entity/AccessorTest.php index d3ffdcdbd..95fb3e0e3 100644 --- a/tests/Orm/Entity/AccessorTest.php +++ b/tests/Orm/Entity/AccessorTest.php @@ -13,8 +13,6 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase $entity2->username = 'romanb'; $this->assertEquals('romanb?!', $entity1->username); } - - } @@ -41,12 +39,12 @@ class CustomAccessorMutatorTestEntity extends Doctrine_Entity public function getUsernameCustom() { - return $this->_rawGetField('username') . "!"; + return $this->_get('username') . "!"; } public function setUsernameCustom($username) { - $this->_rawSetField('username', $username . "?"); + $this->_set('username', $username . "?"); } } @@ -69,11 +67,11 @@ class MagicAccessorMutatorTestEntity extends Doctrine_Entity public function getUsername() { - return $this->_rawGetField('username') . "!"; + return $this->_get('username') . "!"; } public function setUsername($username) { - $this->_rawSetField('username', $username . "?"); + $this->_set('username', $username . "?"); } } \ No newline at end of file diff --git a/tests/Orm/Query/SelectSqlGenerationTest.php b/tests/Orm/Query/SelectSqlGenerationTest.php index b8183b371..92c0570f4 100755 --- a/tests/Orm/Query/SelectSqlGenerationTest.php +++ b/tests/Orm/Query/SelectSqlGenerationTest.php @@ -134,7 +134,9 @@ class Orm_Query_SelectSqlGenerationTest extends Doctrine_OrmTestCase { $this->assertSqlGeneration( "SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'", - "SELECT cu.name AS cu__name FROM cms_user cu WHERE TRIM(cu.name) = ''someone''" // SQLite double slashes for strings + // String quoting in the SQL usually depends on the database platform. + // This test works with a mock connection which uses ' for string quoting. + "SELECT cu.name AS cu__name FROM cms_user cu WHERE TRIM(cu.name) = 'someone'" ); }