Commit df0511e9 authored by zYne's avatar zYne

Updated some docs, added identifier quoting to DQL, fixed some test cases,...

Updated some docs, added identifier quoting to DQL, fixed some test cases, fixed dql select part handling
parent cb20dfaf
......@@ -31,6 +31,45 @@
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
*/
class Doctrine_DataDict extends Doctrine_Connection_Module {
/**
* Obtain an array of changes that may need to applied
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
*/
public function compareDefinition($current, $previous) {
$type = !empty($current['type']) ? $current['type'] : null;
}
if (!method_exists($this, "_compare{$type}Definition")) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
}
if (empty($previous['type']) || $previous['type'] != $type) {
return $current;
}
$change = $this->{"_compare{$type}Definition"}($current, $previous);
if ($previous['type'] != $type) {
$change['type'] = true;
}
$previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
$notnull = !empty($current['notnull']) ? $current['notnull'] : false;
if ($previous_notnull != $notnull) {
$change['notnull'] = true;
}
$previous_default = array_key_exists('default', $previous) ? $previous['default'] :
($previous_notnull ? '' : null);
$default = array_key_exists('default', $current) ? $current['default'] :
($notnull ? '' : null);
if ($previous_default !== $default) {
$change['default'] = true;
}
return $change;
}
}
......@@ -50,5 +50,77 @@ class Doctrine_DataDict_Informix extends Doctrine_DataDict {
and i.idxname=s.idxname and tr.tabid=r.ptabid
and s2.constrid=r.primary and i2.idxname=s2.idxname",
);
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
*
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
*/
public function getNativeDeclaration($field) {
switch ($field['type']) {
case 'char':
case 'varchar':
case 'array':
case 'object':
case 'string':
if (empty($field['length']) && array_key_exists('default', $field)) {
$field['length'] = $this->conn->varchar_max_length;
}
$length = (! empty($field['length'])) ? $field['length'] : false;
$fixed = ((isset($field['fixed']) && $field['fixed']) || $field['type'] == 'char') ? true : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR(255)')
: ($length ? 'VARCHAR('.$length.')' : 'NVARCHAR');
case 'clob':
return 'TEXT';
case 'blob':
return 'BLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 1) {
return 'SMALLINT';
} elseif ($length == 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INTEGER';
} elseif ($length > 4) {
return 'DECIMAL(20)';
}
}
return 'INT';
case 'boolean':
return 'SMALLINT';
case 'date':
return 'DATE';
case 'time':
return 'DATETIME YEAR TO SECOND';
case 'timestamp':
return 'DATETIME';
case 'float':
return 'FLOAT';
case 'decimal':
return 'DECIMAL';
}
return '';
}
}
......@@ -223,10 +223,44 @@ class Doctrine_Export extends Doctrine_Connection_Module {
* 'last_login' => array()
* )
* )
* @throws PDOException
* @return void
*/
public function createIndex($table, $name, array $definition) {
return $this->conn->getDbh()->query($this->createIndexSql($table, $name, $definition));
}
/**
* Get the stucture of a field into an array
*
*
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the index fields as array
* indexes. Each entry of this array is set to another type of associative
* array that specifies properties of the index that are specific to
* each field.
*
* Currently, only the sorting property is supported. It should be used
* to define the sorting direction of the index. It may be set to either
* ascending or descending.
*
* Not all DBMS support index sorting direction configuration. The DBMS
* drivers of those that do not support it ignore this property. Use the
* function supports() to determine whether the DBMS driver can manage indexes.
*
* Example
* array(
* 'fields' => array(
* 'user_name' => array(
* 'sorting' => 'ascending'
* ),
* 'last_login' => array()
* )
* )
* @return string
*/
public function createIndexSql($table, $name, array $definition) {
$table = $this->conn->quoteIdentifier($table);
$name = $this->conn->quoteIdentifier($name);
......@@ -237,10 +271,8 @@ class Doctrine_Export extends Doctrine_Connection_Module {
}
$query .= ' ('. implode(', ', $fields) . ')';
return $this->conn->getDbh()->query($query);
return $query;
}
/**
* alter an existing table
* (this method is implemented by the drivers)
......@@ -331,6 +363,98 @@ class Doctrine_Export extends Doctrine_Connection_Module {
* @return void
*/
public function alterTable($name, array $changes, $check) {
$this->conn->getDbh()->query($this->alterTableSql($name, $changes, $check));
}
/**
* alter an existing table
* (this method is implemented by the drivers)
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @return string
*/
public function alterTableSql($name, array $changes, $check) {
throw new Doctrine_Export_Exception('Alter table not supported by this driver.');
}
/**
......
......@@ -74,10 +74,10 @@ class Doctrine_Export_Sqlite extends Doctrine_Export {
if(isset($field['sorting'])) {
switch ($field['sorting']) {
case 'ascending':
$fieldString.= ' ASC';
$fieldString .= ' ASC';
break;
case 'descending':
$fieldString.= ' DESC';
$fieldString .= ' DESC';
break;
}
}
......
......@@ -55,9 +55,9 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
*/
protected $params = array();
/**
* @var Doctrine_Connection $connection Doctrine_Connection object
* @var Doctrine_Connection $conn Doctrine_Connection object
*/
protected $connection;
protected $conn;
/**
* @var Doctrine_View $view Doctrine_View object
*/
......@@ -115,7 +115,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
if( ! ($connection instanceof Doctrine_Connection))
$connection = Doctrine_Manager::getInstance()->getCurrentConnection();
$this->connection = $connection;
$this->conn = $connection;
$this->aliasHandler = new Doctrine_Hydrate_Alias();
}
/**
......@@ -240,7 +240,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
* @return Doctrine_Connection
*/
public function getConnection() {
return $this->connection;
return $this->conn;
}
/**
* setView
......@@ -340,10 +340,10 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
$query = $this->view->getSelectSql();
if($this->isLimitSubqueryUsed() &&
$this->connection->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql')
$this->conn->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql')
$params = array_merge($params, $params);
$stmt = $this->connection->execute($query, $params);
$stmt = $this->conn->execute($query, $params);
if($this->aggregate)
return $stmt->fetchAll(PDO::FETCH_ASSOC);
......
......@@ -18,6 +18,7 @@
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
*/
Doctrine::autoload('Doctrine_Hydrate');
/**
* Doctrine_Query
*
......@@ -457,7 +458,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
public function getQueryBase() {
switch($this->type) {
case self::DELETE:
if($this->connection->getName() == 'mysql')
if($this->conn->getName() == 'mysql')
$q = 'DELETE '.end($this->tableAliases).' FROM ';
else
$q = 'DELETE FROM ';
......@@ -501,15 +502,13 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
if($this->isDistinct())
$str = 'DISTINCT ';
$q = 'SELECT '.$str.implode(", ",$this->parts["select"]).
' FROM ';
$q = $this->getQueryBase();
$q .= $this->parts['from'];
if( ! empty($this->parts['join'])) {
foreach($this->parts['join'] as $part) {
$q .= " ".implode(' ', $part);
$q .= ' '.implode(' ', $part);
}
}
......@@ -527,10 +526,10 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$subquery = $this->getLimitSubquery();
switch(strtolower($this->connection->getName())) {
switch(strtolower($this->conn->getName())) {
case 'mysql':
// mysql doesn't support LIMIT in subqueries
$list = $this->connection->execute($subquery, $params)->fetchAll(PDO::FETCH_COLUMN);
$list = $this->conn->execute($subquery, $params)->fetchAll(PDO::FETCH_COLUMN);
$subquery = implode(', ', $list);
break;
case 'pgsql':
......@@ -555,7 +554,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$q .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(' ', $this->parts['orderby']):'';
if($modifyLimit)
$q = $this->connection->modifyLimitQuery($q, $this->parts['limit'], $this->parts['offset']);
$q = $this->conn->modifyLimitQuery($q, $this->parts['limit'], $this->parts['offset']);
// return to the previous state
if( ! empty($string))
......@@ -586,7 +585,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
// initialize the base of the subquery
$subquery = 'SELECT DISTINCT ' . $primaryKey;
if($this->connection->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'pgsql') {
if($this->conn->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'pgsql') {
// pgsql needs the order by fields to be preserved in select clause
foreach($this->parts['orderby'] as $part) {
......@@ -623,7 +622,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$subquery .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(' ', $this->parts['orderby']):'';
// add driver specific limit clause
$subquery = $this->connection->modifyLimitQuery($subquery, $this->parts['limit'], $this->parts['offset']);
$subquery = $this->conn->modifyLimitQuery($subquery, $this->parts['limit'], $this->parts['offset']);
$parts = self::quoteExplode($subquery, ' ', "'", "'");
......@@ -1059,7 +1058,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
if($key == 0) {
$currPath = substr($currPath,1);
$table = $this->connection->getTable($name);
$table = $this->conn->getTable($name);
$tname = $this->aliasHandler->getShortAlias($table->getTableName());
......@@ -1068,7 +1067,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$this->tableIndexes[$tname] = 1;
$this->parts["from"] = $table->getTableName() . ' ' . $tname;
$this->parts["from"] = $this->conn->quoteIdentifier($table->getTableName()) . ' ' . $tname;
$this->tableAliases[$currPath] = $tname;
......@@ -1097,7 +1096,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
} else
$tname2 = $this->aliasHandler->generateShortAlias($original);
$aliasString = $original . ' ' . $tname2;
$aliasString = $this->conn->quoteIdentifier($original) . ' ' . $tname2;
switch($mark) {
case ':':
......@@ -1113,7 +1112,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
if( ! $fk->isOneToOne()) {
$this->needsSubquery = true;
}
if( ! $loadFields || $fk->getTable()->usesInheritanceMap()) {
$this->subqueryAliases[] = $tname2;
}
......@@ -1126,16 +1125,17 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
if( ! $loadFields) {
$this->subqueryAliases[] = $assocTableName;
}
$this->parts["join"][$tname][$assocTableName] = $join.$assocTableName . ' ON ' .$tname . '.'
$this->parts["join"][$tname][$assocTableName] = $join . $assocTableName . ' ON ' . $tname . '.'
. $table->getIdentifier() . ' = '
. $assocTableName . '.' . $fk->getLocal();
$this->parts["join"][$tname][$tname2] = $join.$aliasString . ' ON ' .$tname2 . '.'
$this->parts["join"][$tname][$tname2] = $join . $aliasString . ' ON ' . $tname2 . '.'
. $table->getIdentifier() . ' = '
. $assocTableName . '.' . $fk->getForeign();
} else {
$this->parts["join"][$tname][$tname2] = $join.$aliasString . ' ON ' .$tname . '.'
$this->parts["join"][$tname][$tname2] = $join . $aliasString
. ' ON ' . $tname . '.'
. $fk->getLocal() . ' = ' . $tname2 . '.' . $fk->getForeign();
}
......@@ -1160,8 +1160,12 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$this->tables[$tableName] = $table;
if($loadFields) {
$skip = false;
if( ! empty($this->pendingFields))
$skip = true;
if($componentAlias) {
$this->compAliases[$componentAlias] = $currPath;
......@@ -1174,6 +1178,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
$skip = true;
}
}
if( ! $skip)
$this->parseFields($fullname, $tableName, $e2, $currPath);
}
......
......@@ -22,14 +22,14 @@ Doctrine::autoload('Doctrine_Hydrate');
/**
* Doctrine_RawSql
*
* @package Doctrine
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @category Object Relational Mapping
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
* @package Doctrine
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @category Object Relational Mapping
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
class Doctrine_RawSql extends Doctrine_Hydrate {
/**
* @var array $fields
......@@ -217,9 +217,9 @@ class Doctrine_RawSql extends Doctrine_Hydrate {
$tableName = $table->getAliasName($component);
$table = $this->connection->getTable($tableName);
$table = $this->conn->getTable($tableName);
} else {
$table = $this->connection->getTable($component);
$table = $this->conn->getTable($component);
}
$this->tables[$alias] = $table;
$this->fetchModes[$alias] = Doctrine::FETCH_IMMEDIATE;
......
......@@ -1364,6 +1364,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$this->_table->getOption($name);
else
$this->_table->setOption($name, $value);
}
public function hasIndex($name ) {
}
/**
* addListener
......
......@@ -19,17 +19,10 @@
* <http://www.phpdoctrine.com>.
*/
Doctrine::autoload('Doctrine_Access');
/**
* @package Doctrine
* @url http://www.phpdoctrine.com
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Jukka Hassinen <Jukka.Hassinen@BrainAlliance.com>
* @author Konsta Vesterinen
* @version $Id$
*/
/**
* class Doctrine_Schema_Object
* Catches any non-property call from child classes and throws an exception.
*
* @package Doctrine
* @category Object Relational Mapping
* @link www.phpdoctrine.com
......@@ -37,6 +30,7 @@ Doctrine::autoload('Doctrine_Access');
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Jukka Hassinen <Jukka.Hassinen@BrainAlliance.com>
*/
abstract class Doctrine_Schema_Object extends Doctrine_Access implements IteratorAggregate, Countable {
......
......@@ -775,7 +775,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable {
* @return string
*/
final public function getTableName() {
return $this->conn->quoteIdentifier($this->options['tableName']);
return $this->options['tableName'];
}
/**
* create
......
<?php
// find all users
$coll = $conn->query("FROM User");
// find all users with only their names (and primary keys) fetched
$coll = $conn->query("FROM User(name)");
// find all groups
$coll = $conn->query("FROM Group");
$coll = $q->from("FROM Group");
// find all users and user emails
$coll = $conn->query("FROM User.Email");
$coll = $q->from("FROM User.Email");
// find all users and user emails with only user name and
// find all users and user emails with only user name and
// age + email address loaded
$coll = $conn->query("FROM User(name, age).Email(address)");
$coll = $q->select('u.name, u.age, e.address')
->from('FROM User u')
->leftJoin('u.Email e')
->execute();
// find all users, user email and user phonenumbers
$coll = $conn->query("FROM User.Email, User.Phonenumber");
$coll = $q->from('FROM User u')
->leftJoin('u.Email e')
->leftJoin('u.Phonenumber p')
->execute();
?>
DQL FROM -part is used for selecting tables as well as for selecting fields. Related components are selected either
with colon-operator or dot-operator (See <a href="documentation.php?index=2.8.php">Relation operators</a>). <br \>You can place
the selected fields in () -brackets (eg. 'FROM User(name, id)'). If you are about to select all fields you can simple use 'FROM User'.
<?php ?>
The FROM clause indicates the component or components from which to retrieve records.
If you name more than one component, you are performing a join.
For each table specified, you can optionally specify an alias. Doctrine_Query offers easy to use
methods such as from(), addFrom(), leftJoin() and innerJoin() for managing the FROM part of your DQL query.
<?php
renderCode("<?php
// find all users
\$q = new Doctrine_Query();
\$coll = \$q->from('User')->execute();
// find all users with only their names (and primary keys) fetched
\$coll = \$q->select('u.name')->('User u');
?>");
?>
<pre>
FEATURES:
<?php
include("top.php");
?>
GENERAL FEATURES
- Fully object-oriented following best practices and design patterns
- Multiple databases
- Database connection pooling with connection-record -registry
- Runtime configuration (no XML needed!)
- Very modular structure (only uses the needed features)
- The whole framework can be compiled into a single file
- Leveled configuration (attributes can be set at global, connection and table levels)
DATABASE ABSTRACTION:
- A DSN (data source name) or array format for specifying database servers
- Datatype abstraction and on demand datatype conversion
- supports PDO
- Database query profiling
- Query caching
- Sequence / autoincrement emulation
- Replace emulation
- RDBMS management methods (creating, dropping, altering)
- SQL function call abstraction
- SQL expression abstraction
- Pattern matching abstraction
- Portable error codes
- Nested transactions
- Transaction isolation abstraction
- Transaction savepoint abstraction
- Index/Unique Key/Primary Key support
- Ability to read the information schema
- Reverse engineering schemas from an existing database
- LIMIT / OFFSET emulation
<table width="100%" cellspacing=0 cellpadding=0>
<tr>
<td width=50>
<td>
<td align="left" valign="top">
<table width="100%" cellspacing=1 cellpadding=1>
<tr>
<td bgcolor="white">
<img src="images/logo.jpg" align="left"><b class="title">Doctrine - PHP Data Persistence and ORM Tool</b>
<hr>
</td>
</tr>
<tr>
<td>
OBJECT RELATIONAL MAPPING:
General features:
- Validators
- Transactional errorStack for easy retrieval of all errors
- EventListeners
- UnitOfWork pattern (easy saving of all pending objects)
- Uses ActiveRecord pattern
- State-wise records and transactions
- Importing existing database schemas to Doctrine ActiveRecord objects
- Exporting Doctrine ActiveRecords to database (= automatic table creation)
Mapping:
- Composite, Natural, Autoincremented and Sequential identifiers
- PHP Array / Object data types for columns (automatic serialization/unserialization)
- Gzip datatype for all databases
- Emulated enum datatype for all databases
- Datatype abstraction
- Column aggregation inheritance
- One-class-one-table inheritance as well as One-table
- One-to-many, many-to-one, one-to-one and many-to-many relations
- Self-referencing relations even for association table relations
- Relation aliases
Object population:
- DQL (Doctrine Query Language), an EJB 3 spec compliant OQL
- <b>The limit-subquery-algorithm</b>
- OO-style query API for both DQL and raw SQL
- Object population from database views
- Object population through raw SQL
Transactions and locking:
- Pessimistic offline locking
- Savepoints, transaction isolation levels and nested transactions
- Transactional query optimization (gathering of DELETE statements)
</pre>
<ul>
<b class='title'>GENERAL FEATURES</b>
<ul>
<li \> Fully object-oriented following best practices and design patterns
<li \>Multiple databases
<li \>Database connection pooling with connection-record -registry
<li \>Runtime configuration (no XML needed!)
<li \>Very modular structure (only uses the needed features)
<li \>The whole framework can be compiled into a single file
<li \>Leveled configuration (attributes can be set at global, connection and table levels)
</ul>
<br \>
<b class='title'>DATABASE ABSTRACTION</b>
<ul>
<li \>A DSN (data source name) or array format for specifying database servers
<li \>Datatype abstraction and on demand datatype conversion
<li \>supports PDO
<li \>Database query profiling
<li \>Query caching
<li \>Sequence / autoincrement emulation
<li \>Replace emulation
<li \>RDBMS management methods (creating, dropping, altering)
<li \>SQL function call abstraction
<li \>SQL expression abstraction
<li \>Pattern matching abstraction
<li \>Portable error codes
<li \>Nested transactions
<li \>Transaction isolation abstraction
<li \>Transaction savepoint abstraction
<li \>Index/Unique Key/Primary Key support
<li \>Ability to read the information schema
<li \>Reverse engineering schemas from an existing database
<li \>LIMIT / OFFSET emulation
</ul>
<br \>
<b class='title'>OBJECT RELATIONAL MAPPING</b>:
<ul>
<b class='title'>General features</b>
<li \>Validators
<li \>Transactional errorStack for easy retrieval of all errors
<li \>EventListeners
<li \>UnitOfWork pattern (easy saving of all pending objects)
<li \>Uses ActiveRecord pattern
<li \>State-wise records and transactions
<li \>Importing existing database schemas to Doctrine ActiveRecord objects
<li \>Exporting Doctrine ActiveRecords to database (= automatic table creation)
<br \><br \>
<b class='title'>Mapping</b>
<li \>Composite, Natural, Autoincremented and Sequential identifiers
<li \>PHP Array / Object data types for columns (automatic serialization/unserialization)
<li \>Gzip datatype for all databases
<li \>Emulated enum datatype for all databases
<li \>Datatype abstraction
<li \>Column aggregation inheritance
<li \>One-class-one-table inheritance as well as One-table
<li \>One-to-many, many-to-one, one-to-one and many-to-many relations
<li \>Self-referencing relations even for association table relations
<li \>Relation aliases
<br \><br \>
<b class='title'>Object population</b>
<li \>DQL (Doctrine Query Language), an EJB 3 spec compliant OQL
<li \><b>The limit-subquery-algorithm</b>
<li \>OO-style query API for both DQL and raw SQL
<li \>Object population from database views
<li \>Object population through raw SQL
<br \><br \>
<b class='title'>Transactions and locking</b>
<li \>Pessimistic offline locking
<li \>Savepoints, transaction isolation levels and nested transactions
<li \>Transactional query optimization (gathering of DELETE statements)
</ul>
</ul>
</td>
</tr>
</tr>
</table>
......@@ -10,7 +10,7 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
public function testLimitWithOneToOneLeftJoin() {
$q = new Doctrine_Query($this->connection);
$q->from('User(id).Email')->limit(5);
$q->select('u.id, e.*')->from('User u, u.Email e')->limit(5);
$users = $q->execute();
$this->assertEqual($users->count(), 5);
......@@ -19,15 +19,14 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
}
public function testLimitWithOneToOneInnerJoin() {
$q = new Doctrine_Query($this->connection);
$q->from('User(id):Email')->limit(5);
$q->select('u.id, e.*')->from('User u, u:Email e')->limit(5);
$users = $q->execute();
$this->assertEqual($users->count(), 5);
$this->assertEqual($q->getQuery(), "SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__address FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0) LIMIT 5");
}
public function testLimitWithOneToManyLeftJoin() {
$this->query->from("User(id).Phonenumber");
$this->query->limit(5);
$this->query->select('u.id, p.*')->from('User u, u.Phonenumber p')->limit(5);
$sql = $this->query->getQuery();
$this->assertEqual($this->query->getQuery(),
......@@ -87,7 +86,7 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
}
public function testLimitWithOneToManyInnerJoin() {
$this->query->select('u.id')->from("User u INNER JOIN u.Phonenumber");
$this->query->select('u.id, p.*')->from('User u INNER JOIN u.Phonenumber p');
$this->query->limit(5);
......@@ -114,8 +113,8 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
public function testLimitWithPreparedQueries() {
$q = new Doctrine_Query();
$q->from("User(id).Phonenumber(id)");
$q->where("User.name = ?");
$q->select('u.id, p.id')->from('User u LEFT JOIN u.Phonenumber p');
$q->where("u.name = ?");
$q->limit(5);
$users = $q->execute(array('zYne'));
......@@ -128,7 +127,7 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
'SELECT e.id AS e__id, p.id AS p__id FROM entity e LEFT JOIN phonenumber p ON e.id = p.entity_id WHERE e.id IN (SELECT DISTINCT e2.id FROM entity e2 WHERE e2.name = ? AND (e2.type = 0) LIMIT 5) AND e.name = ? AND (e.type = 0)');
$q = new Doctrine_Query();
$q->from("User(id).Phonenumber(id)");
$q->select('u.id, p.id')->from('User u LEFT JOIN u.Phonenumber p');
$q->where("User.name LIKE ? || User.name LIKE ?");
$q->limit(5);
$users = $q->execute(array('%zYne%', '%Arnold%'));
......
......@@ -17,7 +17,7 @@ class Doctrine_Query_Select_TestCase extends Doctrine_UnitTestCase {
$this->assertEqual($q->getQuery(), 'SELECT COUNT(e.id) AS e__0 FROM entity e WHERE (e.type = 0)');
}
public function testMultipleAggregateFunctions() {
public function testSelectPartSupportsMultipleAggregateFunctions() {
$q = new Doctrine_Query();
$q->parseQuery('SELECT MAX(u.id), MIN(u.name) FROM User u');
......
......@@ -43,6 +43,7 @@ require_once('QuerySelectTestCase.php');
require_once('QueryShortAliasesTestCase.php');
require_once('QueryDeleteTestCase.php');
require_once('QueryUpdateTestCase.php');
require_once('QueryIdentifierQuotingTestCase.php');
require_once('UnitOfWorkTestCase.php');
......@@ -146,8 +147,8 @@ $test->addTestCase(new Doctrine_Export_Firebird_TestCase());
$test->addTestCase(new Doctrine_Configurable_TestCase());
*/
*/
$test->addTestCase(new Doctrine_Transaction_TestCase());
......@@ -258,6 +259,7 @@ $test->addTestCase(new Doctrine_Query_Limit_TestCase());
$test->addTestCase(new Doctrine_Query_Select_TestCase());
$test->addTestCase(new Doctrine_Query_IdentifierQuoting_TestCase());
//$test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase());
......
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