1
0
mirror of synced 2025-01-18 22:41:43 +03:00

Updated some docs, added identifier quoting to DQL, fixed some test cases, fixed dql select part handling

This commit is contained in:
zYne 2006-12-03 20:05:09 +00:00
parent cb20dfafc7
commit df0511e9dc
16 changed files with 419 additions and 141 deletions

View File

@ -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;
}
}

View File

@ -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 '';
}
}

View File

@ -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.');
}
/**

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -1,27 +1,24 @@
<?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();
?>

View File

@ -1,3 +1,18 @@
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');
?>");
?>

View File

@ -1,69 +1,97 @@
<pre>
FEATURES:
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
<?php
include("top.php");
?>
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)
<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>
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>

View File

@ -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%'));

View File

@ -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');

View File

@ -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());