1
0
mirror of synced 2024-12-05 03:06:05 +03:00

[2.0][DDC-284] Fixed. API polish and some convention over configuration simplifications for join columns and join tables.

This commit is contained in:
romanb 2010-02-09 17:13:49 +00:00
parent da2c329e60
commit 4adc289596
33 changed files with 415 additions and 382 deletions

View File

@ -113,11 +113,11 @@
<xs:enumeration value="EAGER"/>
<xs:enumeration value="LAZY"/>
</xs:restriction>
</xs:simpleType>
</xs:simpleType>
<xs:complexType name="field">
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
<xs:attribute name="column" type="xs:NMTOKEN" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
@ -167,7 +167,7 @@
</xs:complexType>
<xs:complexType name="generator">
<xs:attribute name="strategy" type="orm:generator-strategy" use="required" />
<xs:attribute name="strategy" type="orm:generator-strategy" use="optional" default="AUTO" />
</xs:complexType>
<xs:complexType name="id">
@ -187,7 +187,7 @@
<xs:complexType name="join-column">
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
<xs:attribute name="nullable" type="xs:boolean" default="true" />
<xs:attribute name="on-delete" type="orm:fk-action" />
@ -213,7 +213,7 @@
<xs:complexType name="many-to-many">
<xs:sequence>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="join-table" type="orm:join-table" />
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
</xs:sequence>
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
@ -235,7 +235,7 @@
<xs:complexType name="many-to-one">
<xs:sequence>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:choice minOccurs="1" maxOccurs="1">
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="join-column" type="orm:join-column"/>
<xs:element name="join-columns" type="orm:join-columns"/>
</xs:choice>

View File

@ -52,4 +52,14 @@ class Annotation
$this->$key = $value;
}
}
public function __get($name)
{
throw new \BadMethodCallException("Unknown annotation property '$name' on annotation '".get_class($this)."'.");
}
public function __set($name, $value)
{
throw new \BadMethodCallException("Unknown annotation property '$name' on annotation '".get_class($this)."'.");
}
}

View File

@ -82,7 +82,6 @@ class Configuration
*
* @param array $types Key-value map of types to include
* @param boolean $override Optional flag to support only inclusion or also override
* @throws DoctrineException
*/
public function setCustomTypes(array $types, $override = false)
{
@ -97,7 +96,6 @@ class Configuration
* Overrides existent types in Doctrine
*
* @param array $types Key-value map of types to override
* @throws DoctrineException
*/
public function setTypeOverrides(array $overrides)
{

View File

@ -22,7 +22,6 @@
namespace Doctrine\DBAL;
use Doctrine\Common\EventManager,
Doctrine\Common\DoctrineException,
Doctrine\DBAL\DBALException;
/**

View File

@ -21,8 +21,6 @@
namespace Doctrine\DBAL;
use Doctrine\Common\DoctrineException;
/**
* Doctrine\DBAL\ConnectionException
*
@ -32,20 +30,15 @@ use Doctrine\Common\DoctrineException;
* @version $Revision: 4628 $
* @author Jonathan H. Wage <jonwage@gmail.com
*/
class ConnectionException extends DoctrineException
class ConnectionException extends DBALException
{
public static function invalidPDOInstance()
public static function commitFailedRollbackOnly()
{
return new self("Invalid PDO instance provided on connection creation.");
}
public static function driverRequired()
{
return new self("Please provide a driver or a driverClass to be able to start a Connection.");
return new self("Transaction commit failed because the transaction has been marked for rollback only.");
}
public static function unknownDriver($driver)
public static function noActiveTransaction()
{
return new self("Unknown Connection driver '$driver'.");
return new self("There is no active transaction.");
}
}

View File

@ -21,7 +21,6 @@
namespace Doctrine\DBAL;
use Doctrine\Common\DoctrineException;
use Doctrine\Common\EventManager;
/**

View File

@ -147,7 +147,7 @@ abstract class AbstractQuery
}
/**
* Get all defined parameters
* Get all defined parameters.
*
* @return array Defined parameters
*/

View File

@ -22,7 +22,6 @@
namespace Doctrine\ORM;
use Doctrine\Common\EventManager,
Doctrine\Common\DoctrineException,
Doctrine\DBAL\Connection,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataFactory,
@ -96,6 +95,11 @@ class EntityManager
* @var Doctrine\ORM\Proxy\ProxyFactory
*/
private $_proxyFactory;
/**
* @var ExpressionBuilder The expression builder instance used to generate query expressions.
*/
private $_expressionBuilder;
/**
* Whether the EntityManager is closed or not.
@ -144,6 +148,27 @@ class EntityManager
return $this->_metadataFactory;
}
/**
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
*
* Example:
*
* [php]
* $qb = $em->createQueryBuilder();
* $expr = $em->getExpressionBuilder();
* $qb->select('u')->from('User', 'u')
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
*
* @return ExpressionBuilder
*/
public function getExpressionBuilder()
{
if ($this->_expressionBuilder === null) {
$this->_expressionBuilder = new Query\Expr;
}
return $this->_expressionBuilder;
}
/**
* Starts a transaction on the underlying database connection.
*/

View File

@ -2,7 +2,7 @@
namespace Doctrine\ORM\Internal\Hydration;
class HydrationException extends \Doctrine\Common\DoctrineException
class HydrationException extends \Doctrine\ORM\ORMException
{
public static function nonUniqueResult()
{

View File

@ -129,6 +129,7 @@ abstract class AssociationMapping
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping
* @throws MappingException If something is wrong with the mapping.
*/
protected function _validateAndCompleteMapping(array $mapping)
{
@ -151,7 +152,7 @@ abstract class AssociationMapping
// Mandatory and optional attributes for either side
if ( ! isset($mapping['mappedBy'])) {
// Optional
if (isset($mapping['joinTable'])) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
if ($mapping['joinTable']['name'][0] == '`') {
$mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
$mapping['joinTable']['quoted'] = true;
@ -164,8 +165,7 @@ abstract class AssociationMapping
}
// Optional attributes for both sides
$this->fetchMode = isset($mapping['fetch']) ?
$mapping['fetch'] : self::FETCH_LAZY;
$this->fetchMode = isset($mapping['fetch']) ? $mapping['fetch'] : self::FETCH_LAZY;
$cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
if (in_array('all', $cascades)) {
@ -178,11 +178,11 @@ abstract class AssociationMapping
);
}
$this->isCascadeRemove = in_array('remove', $cascades);
$this->isCascadeRemove = in_array('remove', $cascades);
$this->isCascadePersist = in_array('persist', $cascades);
$this->isCascadeRefresh = in_array('refresh', $cascades);
$this->isCascadeMerge = in_array('merge', $cascades);
$this->isCascadeDetach = in_array('detach', $cascades);
$this->isCascadeMerge = in_array('merge', $cascades);
$this->isCascadeDetach = in_array('detach', $cascades);
}
/**

View File

@ -254,15 +254,6 @@ class ClassMetadataInfo
*/
public $columnNames = array();
/**
* Whether to automatically OUTER JOIN subtypes when a basetype is queried.
*
* <b>This does only apply to the JOINED inheritance mapping strategy.</b>
*
* @var boolean
*/
//public $joinSubclasses = true;
/**
* The discriminator value of this class.
*
@ -270,7 +261,7 @@ class ClassMetadataInfo
* where a discriminator column is used.</b>
*
* @var mixed
* @see _discriminatorColumn
* @see discriminatorColumn
*/
public $discriminatorValue;
@ -281,7 +272,7 @@ class ClassMetadataInfo
* where a discriminator column is used.</b>
*
* @var mixed
* @see _discriminatorColumn
* @see discriminatorColumn
*/
public $discriminatorMap = array();
@ -670,7 +661,8 @@ class ClassMetadataInfo
throw MappingException::missingFieldName($this->name, $mapping);
}
if ( ! isset($mapping['type'])) {
throw MappingException::missingType($this->name, $mapping);
// Default to string
$mapping['type'] = 'string';
}
// Complete fieldName and columnName mapping
@ -734,6 +726,7 @@ class ClassMetadataInfo
* entity classes that have a single-field pk.
*
* @return string
* @throws MappingException If the class has a composite primary key.
*/
public function getSingleIdentifierFieldName()
{
@ -748,6 +741,7 @@ class ClassMetadataInfo
* entity classes that have a single-field pk.
*
* @return string
* @throws MappingException If the class has a composite primary key.
*/
public function getSingleIdentifierColumnName()
{
@ -776,16 +770,6 @@ class ClassMetadataInfo
return isset($this->fieldMappings[$fieldName]);
}
public function hasInheritedMapping($fieldName)
{
if (isset($this->fieldMappings[$fieldName]) || isset($this->associationMappings[$fieldName]))
{
} else {
return false;
}
}
/**
* Gets all field mappings.
*
@ -1008,7 +992,7 @@ class ClassMetadataInfo
/**
* Sets the mapped subclasses of this class.
*
* @param array $subclasses The names of all mapped subclasses.
* @param array $subclasses The names of all mapped subclasses.
*/
public function setSubclasses(array $subclasses)
{
@ -1337,33 +1321,6 @@ class ClassMetadataInfo
{
return $this->customRepositoryClassName;
}
/**
* Sets whether sub classes should be automatically OUTER JOINed when a base
* class is queried in a class hierarchy that uses the JOINED inheritance mapping
* strategy.
*
* <b>This options does only apply to the JOINED inheritance mapping strategy.</b>
*
* @param boolean $bool
* @see getJoinSubClasses()
*/
/*public function setJoinSubClasses($bool)
{
$this->joinSubclasses = (bool)$bool;
}*/
/**
* Gets whether the class mapped by this instance should OUTER JOIN sub classes
* when a base class is queried.
*
* @return <type>
* @see setJoinSubClasses()
*/
/*public function getJoinSubClasses()
{
return $this->joinSubclasses;
}*/
/**
* Dispatches the lifecycle event of the given entity to the registered
@ -1608,7 +1565,6 @@ class ClassMetadataInfo
* value to use depending on the column type
*
* @param array $mapping The version field mapping array
* @return void
*/
public function setVersionMapping(array &$mapping)
{

View File

@ -40,13 +40,13 @@ final class DiscriminatorMap extends Annotation {}
/*final class SubClasses extends Annotation {}*/
final class Id extends Annotation {}
final class GeneratedValue extends Annotation {
public $strategy;
public $strategy = 'AUTO';
}
final class Version extends Annotation {}
final class JoinColumn extends Annotation {
public $name;
public $fieldName; // field name used in non-object hydration (array/scalar)
public $referencedColumnName;
public $referencedColumnName = 'id';
public $unique = false;
public $nullable = true;
public $onDelete;
@ -55,10 +55,12 @@ final class JoinColumn extends Annotation {
}
final class JoinColumns extends Annotation {}
final class Column extends Annotation {
public $type;
public $type = 'string';
public $length;
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
// The precision for a decimal (exact numeric) column (Applies only for decimal column)
public $precision = 0;
// The scale for a decimal (exact numeric) column (Applies only for decimal column)
public $scale = 0;
public $unique = false;
public $nullable = false;
public $name;
@ -132,5 +134,3 @@ final class PreRemove extends Annotation {}
final class PostRemove extends Annotation {}
final class PostLoad extends Annotation {}
/* Generic annotation for Doctrine extensions */
final class DoctrineX extends Annotation {}

View File

@ -187,8 +187,10 @@ class XmlDriver extends AbstractFileDriver
$metadata->mapField($mapping);
if (isset($idElement->generator)) {
$strategy = isset($idElement->generator['strategy']) ?
(string)$idElement->generator['strategy'] : 'AUTO';
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
. strtoupper((string)$idElement->generator['strategy'])));
. $strategy));
}
// Check for SequenceGenerator/TableGenerator definition
@ -227,8 +229,6 @@ class XmlDriver extends AbstractFileDriver
foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
$mapping['joinColumns'] = $joinColumns;
@ -295,8 +295,6 @@ class XmlDriver extends AbstractFileDriver
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
$mapping['joinColumns'] = $joinColumns;
@ -346,8 +344,6 @@ class XmlDriver extends AbstractFileDriver
}
$mapping['joinTable'] = $joinTable;
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
if (isset($manyToManyElement->cascade)) {

View File

@ -249,8 +249,6 @@ class YamlDriver extends AbstractFileDriver
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
$mapping['joinColumns'] = $joinColumns;
@ -309,8 +307,6 @@ class YamlDriver extends AbstractFileDriver
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
$mapping['joinColumns'] = $joinColumns;
@ -364,8 +360,6 @@ class YamlDriver extends AbstractFileDriver
}
$mapping['joinTable'] = $joinTable;
} else {
throw MappingException::invalidMapping($mapping['fieldName']);
}
if (isset($manyToManyElement['cascade'])) {

View File

@ -32,6 +32,9 @@ namespace Doctrine\ORM\Mapping;
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
@ -78,18 +81,36 @@ class ManyToManyMapping extends AssociationMapping
parent::_validateAndCompleteMapping($mapping);
if ($this->isOwningSide) {
// owning side MUST have a join table
if ( ! isset($mapping['joinTable'])) {
throw MappingException::joinTableRequired($mapping['fieldName']);
if ( ! isset($mapping['joinTable']) || ! $mapping['joinTable']) {
// Apply default join table
$sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1);
$targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1);
$mapping['joinTable'] = array(
'name' => $sourceShortName .'_' . $targetShortName,
'joinColumns' => array(
array(
'name' => $sourceShortName . '_id',
'referencedColumnName' => 'id'
)
),
'inverseJoinColumns' => array(
array(
'name' => $targetShortName . '_id',
'referencedColumnName' => 'id'
)
)
);
$this->joinTable = $mapping['joinTable'];
}
// owning side MUST specify joinColumns
if ( ! isset($mapping['joinTable']['joinColumns'])) {
else if ( ! isset($mapping['joinTable']['joinColumns'])) {
throw MappingException::missingRequiredOption(
$this->sourceFieldName, 'joinColumns',
'Did you think of case sensitivity / plural s?'
);
}
// owning side MUST specify inverseJoinColumns
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
else if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
throw MappingException::missingRequiredOption(
$this->sourceFieldName, 'inverseJoinColumns',
'Did you think of case sensitivity / plural s?'
@ -151,7 +172,9 @@ class ManyToManyMapping extends AssociationMapping
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
$joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn];
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
} else {
@ -162,7 +185,9 @@ class ManyToManyMapping extends AssociationMapping
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
$joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn];
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
}

View File

@ -36,6 +36,9 @@ namespace Doctrine\ORM\Mapping;
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
@ -47,13 +50,6 @@ class OneToManyMapping extends AssociationMapping
public $orphanRemoval = false;
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
//public $keyColumn;
/**
* TODO: Allow any combination of source/target columns in lazy loading.
* What is supported now is primary key (that can spread on multiple fields)
* pointed to foreign keys on the target
public $targetColumns;
*/
/**
* Initializes a new OneToManyMapping.

View File

@ -32,6 +32,9 @@ namespace Doctrine\ORM\Mapping;
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
@ -109,8 +112,12 @@ class OneToOneMapping extends AssociationMapping
}
if ($this->isOwningSide) {
if ( ! isset($mapping['joinColumns'])) {
throw MappingException::invalidMapping($this->sourceFieldName);
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
$mapping['joinColumns'] = array(array(
'name' => $this->sourceFieldName . '_id',
'referencedColumnName' => 'id'
));
}
foreach ($mapping['joinColumns'] as &$joinColumn) {
if ($joinColumn['name'][0] == '`') {
@ -126,7 +133,7 @@ class OneToOneMapping extends AssociationMapping
}
$this->isOptional = isset($mapping['optional']) ?
(bool)$mapping['optional'] : true;
(bool) $mapping['optional'] : true;
$this->orphanRemoval = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
@ -141,7 +148,6 @@ class OneToOneMapping extends AssociationMapping
* Whether the association is optional (0..1), or not (1..1).
*
* @return boolean TRUE if the association is optional, FALSE otherwise.
* @todo Only applicable to OneToOne. Move there.
*/
public function isOptional()
{

View File

@ -21,8 +21,7 @@
namespace Doctrine\ORM;
use Doctrine\Common\DoctrineException,
Doctrine\ORM\Mapping\AssociationMapping,
use Doctrine\ORM\Mapping\AssociationMapping,
\Closure;
/**

View File

@ -184,7 +184,7 @@ class StandardEntityPersister
$this->_assignDefaultVersionValue($this->_class, $entity, $id);
}
}
$stmt->closeCursor();
$this->_queuedInserts = array();
@ -462,7 +462,7 @@ class StandardEntityPersister
foreach ($this->_class->associationMappings as $field => $assoc) {
$value = $this->_class->reflFields[$field]->getValue($entity);
if ($assoc->isOneToOne()) {
if ($value instanceof Proxy && ! $value->__isInitialized()) {
if ($value instanceof Proxy && ! $value->__isInitialized__) {
continue; // skip uninitialized proxies
}

View File

@ -31,155 +31,77 @@ namespace Doctrine\ORM\Query;
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: ExpressionBuilder
*/
class Expr
{
/**
* Creates an instance of Expr\Andx with given arguments.
* Each argument is separated by an "AND". Example:
* Creates a conjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) AND (u.role = ?2)
* $q->where($q->expr()->andx('u.type = ?1', 'u.role = ?2'));
* $expr->andX('u.type = ?1', 'u.role = ?2'));
*
* @param mixed $x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
* @return Expr\Andx
*/
public function andx($x = null)
public function andX($x = null)
{
return new Expr\Andx(func_get_args());
}
/**
* Creates an instance of Expr\Orx with given arguments.
* Each argument is separated by an "OR". Example:
* Creates a disjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) OR (u.role = ?2)
* $q->where($q->expr()->orx('u.type = ?1', 'u.role = ?2'));
* $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));
*
* @param mixed $x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
* @return Expr\Orx
*/
public function orx($x = null)
public function orX($x = null)
{
return new Expr\Orx(func_get_args());
}
/**
* Creates an instance of Expr\Select with given arguments.
* Each argument is separated by a ",". Example:
*
* [php]
* // u.id, u.name, u.surname
* $q->select($q->expr()->select('u.id', 'u.name')->add('u.surname'));
*
* @param mixed $select Optional select. Defaults = null, but requires
* at least one defined when converting to string.
* @return Expr\Select
* Creates an ASCending order expression.
*
* @param $sort
* @return OrderBy
*/
public function select($select = null)
public function asc($expr)
{
return new Expr\Select(is_array($select) ? $select : func_get_args());
return new Expr\OrderBy($expr, 'ASC');
}
/**
* Creates an instance of Expr\From with given arguments.
*
* [php]
* // User u
* $q->from($q->expr()->from('User', 'u'));
*
* @param string $from Entity name.
* @param string $alias Alias to be used by Entity.
* @return Expr\From
* Creates a DESCending order expression.
*
* @param $sort
* @return OrderBy
*/
public function from($from, $alias)
public function desc($expr)
{
return new Expr\From($from, $alias);
}
/**
* Creates an instance of Expr\Join with given arguments.
*
* [php]
* // LEFT JOIN u.Group g WITH g.name = 'admin'
* $q->expr()->leftJoin('u.Group', 'g', 'WITH', "g.name = 'admin'")
*
* @param string $join Relation join.
* @param string $alias Alias to be used by Relation.
* @param string $conditionType Optional type of condition appender. Accepts either string or constant.
* 'ON' and 'WITH' are supported strings. Expr\Join::ON and Expr\Join::WITH are supported constants.
* @param mixed $condition Optional condition to be appended.
* @return Expr\Join
*/
public function leftJoin($join, $alias, $conditionType = null, $condition = null)
{
return new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition);
}
/**
* Creates an instance of Expr\Join with given arguments.
*
* [php]
* // INNER JOIN u.Group g WITH g.name = 'admin'
* $q->expr()->innerJoin('u.Group', 'g', 'WITH', "g.name = 'admin'")
*
* @param string $join Relation join.
* @param string $alias Alias to be used by Relation.
* @param string $conditionType Optional type of condition appender. Accepts either string or constant.
* 'ON' and 'WITH' are supported strings. Expr\Join::ON and Expr\Join::WITH are supported constants.
* @param mixed $condition Optional condition to be appended.
* @return Expr\Join
*/
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
{
return new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition);
return new Expr\OrderBy($expr, 'DESC');
}
/**
* Creates an instance of Expr\OrderBy with given item sort and order.
* Each argument is separated by a ",". Example:
*
* [php]
* $q->orderBy($q->expr()->orderBy('u.surname', 'ASC')->add('u.name', 'ASC'));
*
* @param string $sort Optional item sort.
* @param string $order Optional order to be applied in item.
* @return Expr\OrderBy
*/
public function orderBy($sort = null, $order = null)
{
return new Expr\OrderBy($sort, $order);
}
/**
* Creates an instance of Expr\GroupBy with given arguments.
* Each argument is separated by a ",". Example:
*
* [php]
* // u.id, u.name
* $q->select($q->expr()->groupBy('u.id', 'u.name'));
*
* @param mixed $groupBy Optional group by. Defaults = null, but requires
* at least one defined when converting to string.
* @return Expr\Select
*/
public function groupBy($groupBy = null)
{
return new Expr\GroupBy(func_get_args());
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* Creates an equality comparison expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> = <right expr>. Example:
*
* [php]
* // u.id = ?1
* $q->where($q->expr()->eq('u.id', '?1'));
* $expr->eq('u.id', '?1');
*
* @param mixed $x Left expression
* @param mixed $y Right expression
@ -358,7 +280,7 @@ class Expr
}
/**
* Creates an instance of SOME() function, with the given DQL Subquery.
* Creates a SOME() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in SOME() function.
* @return Expr\Func
@ -369,7 +291,7 @@ class Expr
}
/**
* Creates an instance of ANY() function, with the given DQL subquery.
* Creates an ANY() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in ANY() function.
* @return Expr\Func
@ -380,7 +302,7 @@ class Expr
}
/**
* Creates an instance of NOT() function, with the given restriction.
* Creates a negation expression of the given restriction.
*
* @param mixed $restriction Restriction to be used in NOT() function.
* @return Expr\Func
@ -391,7 +313,7 @@ class Expr
}
/**
* Creates an instance of ABS() function, with the given argument.
* Creates an ABS() function expression with the given argument.
*
* @param mixed $x Argument to be used in ABS() function.
* @return Expr\Func
@ -403,6 +325,7 @@ class Expr
/**
* Creates a product mathematical expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> * <right expr>. Example:
*
@ -461,8 +384,8 @@ class Expr
* When converted to string, it will generated a <left expr> / <right expr>. Example:
*
* [php]
* // u.total - u.period
* $q->expr()->diff('u.total', 'u.period')
* // u.total / u.period
* $expr->quot('u.total', 'u.period')
*
* @param mixed $x Left expression
* @param mixed $y Right expression
@ -474,7 +397,7 @@ class Expr
}
/**
* Creates an instance of SQRT() function, with the given argument.
* Creates a SQRT() function expression with the given argument.
*
* @param mixed $x Argument to be used in SQRT() function.
* @return Expr\Func
@ -485,7 +408,7 @@ class Expr
}
/**
* Creates an instance of field IN() function, with the given arguments.
* Creates an IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IN() function
* @param mixed $y Argument to be used in IN() function.
@ -493,11 +416,18 @@ class Expr
*/
public function in($x, $y)
{
if (is_array($y)) {
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' IN', (array) $y);
}
/**
* Creates an instance of field NOT IN() function, with the given arguments.
* Creates a NOT IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by NOT IN() function
* @param mixed $y Argument to be used in NOT IN() function.
@ -509,7 +439,7 @@ class Expr
}
/**
* Creates an instance of field LIKE() comparison, with the given arguments.
* Creates a LIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
@ -521,7 +451,7 @@ class Expr
}
/**
* Creates an instance of CONCAT() function, with the given argument.
* Creates a CONCAT() function expression with the given arguments.
*
* @param mixed $x First argument to be used in CONCAT() function.
* @param mixed $x Second argument to be used in CONCAT() function.
@ -533,7 +463,7 @@ class Expr
}
/**
* Creates an instance of SUBSTR() function, with the given argument.
* Creates a SUBSTR() function expression with the given arguments.
*
* @param mixed $x Argument to be used as string to be cropped by SUBSTR() function.
* @param integer $from Initial offset to start cropping string. May accept negative values.
@ -546,10 +476,10 @@ class Expr
}
/**
* Creates an instance of LOWER() function, with the given argument.
* Creates a LOWER() function expression with the given argument.
*
* @param mixed $x Argument to be used in LOWER() function.
* @return Expr\Func
* @return Expr\Func A LOWER function expression.
*/
public function lower($x)
{
@ -557,10 +487,10 @@ class Expr
}
/**
* Creates an instance of LOWER() function, with the given argument.
* Creates an UPPER() function expression with the given argument.
*
* @param mixed $x Argument to be used in LOWER() function.
* @return Expr\Func
* @param mixed $x Argument to be used in UPPER() function.
* @return Expr\Func An UPPER function expression.
*/
public function upper($x)
{
@ -568,10 +498,10 @@ class Expr
}
/**
* Creates an instance of LENGTH() function, with the given argument.
* Creates a LENGTH() function expression with the given argument.
*
* @param mixed $x Argument to be used as argument of LENGTH() function.
* @return Expr\Func
* @return Expr\Func A LENGTH function expression.
*/
public function length($x)
{
@ -579,17 +509,28 @@ class Expr
}
/**
* Creates a literal representation of the given argument.
* Creates a literal expression of the given argument.
*
* @param mixed $literal Argument to be converted to literal.
* @return string
* @return Expr\Literal
*/
public function literal($literal)
{
return new Expr\Literal($this->_quoteLiteral($literal));
}
/**
* Quotes a literal value, if necessary, according to the DQL syntax.
*
* @param mixed $literal The literal value.
* @return string
*/
private function _quoteLiteral($literal)
{
if (is_numeric($literal)) {
return (string) $literal;
} else {
return "'" . $literal . "'";
return "'" . str_replace("'", "''", $literal) . "'";
}
}
@ -599,7 +540,7 @@ class Expr
* @param mixed $val Valued to be inspected by range values.
* @param integer $x Starting range value to be used in BETWEEN() function.
* @param integer $y End point value to be used in BETWEEN() function.
* @return Expr\Func
* @return Expr\Func A BETWEEN expression.
*/
public function between($val, $x, $y)
{
@ -610,7 +551,7 @@ class Expr
* Creates an instance of TRIM() function, with the given argument.
*
* @param mixed $x Argument to be used as argument of TRIM() function.
* @return Expr\Func
* @return Expr\Func a TRIM expression.
*/
public function trim($x)
{

View File

@ -61,7 +61,7 @@ abstract class Base
$class = get_class($arg);
if ( ! in_array($class, $this->_allowedClasses)) {
throw \Doctrine\Common\DoctrineException::classNotAllowed($class, $this);
throw new \InvalidArgumentException("Expression of type '$class' not allowed in this context.");
}
}

View File

@ -39,8 +39,8 @@ class Func
public function __construct($name, $arguments)
{
$this->_name = $name;
$this->_arguments = (array) $arguments;
$this->_name = $name;
$this->_arguments = (array) $arguments;
}
public function __toString()

View File

@ -0,0 +1,9 @@
<?php
namespace Doctrine\ORM\Query\Expr;
class Literal extends Base
{
protected $_preSeparator = '';
protected $_postSeparator = '';
}

View File

@ -21,8 +21,7 @@
namespace Doctrine\ORM;
use Doctrine\ORM\Query\Expr,
Doctrine\Common\DoctrineException;
use Doctrine\ORM\Query\Expr;
/**
* This class is responsible for building DQL query strings via an object oriented
@ -46,7 +45,7 @@ class QueryBuilder
const STATE_CLEAN = 1;
/**
* @var EntityManager $em Instance of an EntityManager to use for query.
* @var EntityManager $em The EntityManager used by this QueryBuilder.
*/
private $_em;
@ -78,30 +77,35 @@ class QueryBuilder
* @var string The complete DQL string for this query.
*/
private $_dql;
/**
* @var Query The Query instance used for this QueryBuilder.
* @var array The query parameters.
*/
private $_q;
private $_params = array();
/**
* @var Expr The Expr instance used to generate DQL expressions
* @var integer The index of the first result to retrieve.
*/
private $_expr;
private $_firstResult = null;
/**
* @var integer The maximum number of results to retrieve.
*/
private $_maxResults = null;
/**
* Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
*
* @param EntityManager $entityManager The EntityManager to use.
* @param EntityManager $em The EntityManager to use.
*/
public function __construct(EntityManager $entityManager)
public function __construct(EntityManager $em)
{
$this->_em = $entityManager;
$this->_q = $entityManager->createQuery();
$this->_em = $em;
}
/**
* Factory for instantiating and retrieving the Expr instance when needed
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
* Intended for convenient inline usage. Example:
*
* [php]
* $qb = $em->createQueryBuilder()
@ -109,36 +113,17 @@ class QueryBuilder
* ->from('User', 'u')
* ->where($qb->expr()->eq('u.id', 1));
*
* @return Expr $expr
* @return ExpressionBuilder
*/
public function expr()
{
if ( ! $this->_expr) {
$this->_expr = new Expr;
}
return $this->_expr;
return $this->_em->getExpressionBuilder();
}
/**
* Get the type of this query instance. Either the constant for SELECT, UPDATE or DELETE
* Get the type of the currently built query.
*
* [php]
* switch ($qb->getType())
* {
* case QueryBuilder::SELECT:
* echo 'SELECT';
* break;
*
* case QueryBuilder::DELETE:
* echo 'DELETE';
* break;
*
* case QueryBuilder::UPDATE:
* echo 'UPDATE';
* break;
* }
*
* @return integer $type
* @return integer
*/
public function getType()
{
@ -146,12 +131,9 @@ class QueryBuilder
}
/**
* Get the entity manager instance for this query builder instance
* Get the associated EntityManager for this query builder.
*
* [php]
* $em = $qb->getEntityManager();
*
* @return EntityManager $em
* @return EntityManager
*/
public function getEntityManager()
{
@ -168,7 +150,7 @@ class QueryBuilder
* echo 'Query builder is clean';
* }
*
* @return integer $state
* @return integer
*/
public function getState()
{
@ -184,7 +166,7 @@ class QueryBuilder
* ->from('User', 'u')
* echo $qb->getDql(); // SELECT u FROM User u
*
* @return string $dql The DQL string
* @return string The DQL string
*/
public function getDql()
{
@ -216,7 +198,7 @@ class QueryBuilder
}
/**
* Get the Query instance with the DQL string set to it
* Constructs a Query instance from the current configuration of the builder.
*
* [php]
* $qb = $em->createQueryBuilder()
@ -225,13 +207,14 @@ class QueryBuilder
* $q = $qb->getQuery();
* $results = $q->execute();
*
* @return Query $q
* @return Query
*/
public function getQuery()
{
$this->_q->setDql($this->getDql());
return $this->_q;
return $this->_em->createQuery($this->getDql())
->setParameters($this->_params)
->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults);
}
/**
@ -264,12 +247,11 @@ class QueryBuilder
*
* @param string|integer $key The parameter position or name.
* @param mixed $value The parameter value.
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function setParameter($key, $value)
{
$this->_q->setParameter($key, $value);
$this->_params[$key] = $value;
return $this;
}
@ -287,12 +269,11 @@ class QueryBuilder
* ));
*
* @param array $params
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function setParameters(array $params)
{
$this->_q->setParameters($params);
$this->_params = $params;
return $this;
}
@ -303,7 +284,7 @@ class QueryBuilder
*/
public function getParameters($params = array())
{
return $this->_q->getParameters($params);
return $this->_params;
}
/**
@ -314,18 +295,18 @@ class QueryBuilder
*/
public function getParameter($key)
{
return $this->_q->getParameter($key);
return isset($this->_params[$key]) ? $this->_params[$key] : null;
}
/**
* Sets the position of the first result to retrieve (the "offset").
*
* @param integer $firstResult The first result to return.
* @return QueryBuilder This query builder object.
* @return QueryBuilder This QueryBuilder instance.
*/
public function setFirstResult($firstResult)
{
$this->_q->setFirstResult($firstResult);
$this->_firstResult = $firstResult;
return $this;
}
@ -337,18 +318,18 @@ class QueryBuilder
*/
public function getFirstResult()
{
return $this->_q->getFirstResult();
return $this->_firstResult;
}
/**
* Sets the maximum number of results to retrieve (the "limit").
*
* @param integer $maxResults
* @return QueryBuilder This query builder object.
* @return QueryBuilder This QueryBuilder instance.
*/
public function setMaxResults($maxResults)
{
$this->_q->setMaxResults($maxResults);
$this->_maxResults = $maxResults;
return $this;
}
@ -360,7 +341,7 @@ class QueryBuilder
*/
public function getMaxResults()
{
return $this->_q->getMaxResults();
return $this->_maxResults;
}
/**
@ -369,7 +350,7 @@ class QueryBuilder
* @param string $dqlPartName
* @param string $dqlPart
* @param string $append
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function add($dqlPartName, $dqlPart, $append = false)
{
@ -396,7 +377,7 @@ class QueryBuilder
* ->leftJoin('u.Phonenumbers', 'p');
*
* @param mixed $select String SELECT statement or SELECT Expr instance
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function select($select = null)
{
@ -422,7 +403,7 @@ class QueryBuilder
* ->leftJoin('u.Phonenumbers', 'p');
*
* @param mixed $select String SELECT statement or SELECT Expr instance
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function addSelect($select = null)
{
@ -448,7 +429,7 @@ class QueryBuilder
*
* @param string $delete The model to delete
* @param string $alias The alias of the model
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function delete($delete = null, $alias = null)
{
@ -472,7 +453,7 @@ class QueryBuilder
*
* @param string $update The model to update
* @param string $alias The alias of the model
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function update($update = null, $alias = null)
{
@ -493,17 +474,17 @@ class QueryBuilder
* ->select('u')
* ->from('User', 'u')
*
* @param string $from The model name
* @param string $alias The alias of the model
* @return QueryBuilder $qb
* @param string $from The class name.
* @param string $alias The alias of the class.
* @return QueryBuilder This QueryBuilder instance.
*/
public function from($from, $alias)
{
return $this->add('from', new Expr\From($from, $alias), true);
}
/**
* Add a INNER JOIN
* Add a INNER JOIN to an associated class.
*
* [php]
* $qb = $em->createQueryBuilder()
@ -515,7 +496,27 @@ class QueryBuilder
* @param string $alias The alias of the join
* @param string $conditionType The condition type constant. Either ON or WITH.
* @param string $condition The condition for the join
* @return QueryBuilder $qb
* @return QueryBuilder This QueryBuilder instance.
*/
public function join($join, $alias, $conditionType = null, $condition = null)
{
return $this->innerJoin($join, $alias, $conditionType, $condition);
}
/**
* Add an INNER JOIN to an associated class.
*
* [php]
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u')
* ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
*
* @param string $join The relationship to join
* @param string $alias The alias of the join
* @param string $conditionType The condition type constant. Either ON or WITH.
* @param string $condition The condition for the join
* @return QueryBuilder This QueryBuilder instance.
*/
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
{
@ -756,7 +757,8 @@ class QueryBuilder
*/
public function orderBy($sort, $order = null)
{
return $this->add('orderBy', new Expr\OrderBy($sort, $order));
return $this->add('orderBy', $sort instanceof Expr\OrderBy ? $sort
: new Expr\OrderBy($sort, $order));
}
/**
@ -839,8 +841,8 @@ class QueryBuilder
return $this->getDql();
}
public function __clone()
/*public function __clone()
{
$this->_q = clone $this->_q;
}
}*/
}

View File

@ -40,7 +40,7 @@ class CmsAddress
/**
* @OneToOne(targetEntity="CmsUser")
* @JoinColumn(name="user_id", referencedColumnName="id")
// * @JoinColumn(name="user_id", referencedColumnName="id")
*/
public $user;

View File

@ -12,7 +12,7 @@ class CmsUser
{
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
* @GeneratedValue
*/
public $id;
/**

View File

@ -39,8 +39,8 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa
$user->addGroup($group2);
$this->_em->persist($user); // cascades to groups
$this->_em->flush();
$this->_em->flush();
$this->_em->clear();
$uRep = $this->_em->getRepository(get_class($user));

View File

@ -25,16 +25,15 @@ class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase
/**
* @group DDC-268
*/
public function testColumnWithMissingTypeThrowsException()
public function testColumnWithMissingTypeDefaultsToString()
{
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\InvalidColumn');
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache());
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$annotationDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException',
"The attribute 'type' is required for the column description of property Doctrine\\Tests\\ORM\\Mapping\\InvalidColumn::\$id");
$annotationDriver->loadMetadataForClass('Doctrine\Tests\ORM\Mapping\InvalidColumn', $cm);
$this->assertEquals('string', $cm->fieldMappings['id']['type']);
}
}

View File

@ -86,6 +86,24 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals("DoctrineGlobal_User", $cm->associationMappings['author']->targetEntityName);
}
public function testMapManyToManyJoinTableDefaults()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->mapManyToMany(
array(
'fieldName' => 'groups',
'targetEntity' => 'CmsGroup'
));
$assoc = $cm->associationMappings['groups'];
$this->assertTrue($assoc instanceof \Doctrine\ORM\Mapping\ManyToManyMapping);
$this->assertEquals(array(
'name' => 'CmsUser_CmsGroup',
'joinColumns' => array(array('name' => 'CmsUser_id', 'referencedColumnName' => 'id')),
'inverseJoinColumns' => array(array('name' => 'CmsGroup_id', 'referencedColumnName' => 'id'))
), $assoc->joinTable);
}
/**
* @group DDC-115
@ -141,8 +159,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateAssociationMappingException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$a1 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'joinColumns' => array()));
$a2 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'joinColumns' => array()));
$a1 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo'));
$a2 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo'));
$cm->addAssociationMapping($a1);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');

View File

@ -20,10 +20,8 @@
<field name="email" column="user_email" type="string" column-definition="CHAR(32) NOT NULL" />
<one-to-one field="address" target-entity="Address">
<cascade><cascade-remove /></cascade>
<join-column name="address_id" referenced-column-name="id"/>
<cascade>
<cascade-remove />
</cascade>
</one-to-one>
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user">
@ -33,6 +31,9 @@
</one-to-many>
<many-to-many field="groups" target-entity="Group">
<cascade>
<cascade-all/>
</cascade>
<join-table name="cms_users_groups">
<join-columns>
<join-column name="user_id" referenced-column-name="id" nullable="false" unique="false" />
@ -41,9 +42,6 @@
<join-column name="group_id" referenced-column-name="id" column-definition="INT NULL" />
</inverse-join-columns>
</join-table>
<cascade>
<cascade-all/>
</cascade>
</many-to-many>
</entity>

View File

@ -266,53 +266,29 @@ class ExprTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('(1 = 1) OR (1 < 5)', (string) $orExpr);
}
public function testSelectExpr()
{
$selectExpr = $this->_expr->select();
$selectExpr->add('u.id');
$selectExpr->add('u.username');
$this->assertEquals('u.id, u.username', (string) $selectExpr);
}
public function testFromExpr()
{
$this->assertEquals('User u', (string) $this->_expr->from('User', 'u'));
}
public function testExprBaseCount()
{
$selectExpr = $this->_expr->select();
$selectExpr->add('u.id');
$selectExpr->add('u.username');
$this->assertEquals($selectExpr->count(), 2);
}
public function testOrderByCountExpr()
{
$orderByExpr = $this->_expr->orderBy();
$orderByExpr->add('u.username', 'DESC');
$orderExpr = $this->_expr->desc('u.username');
$this->assertEquals($orderByExpr->count(), 1);
$this->assertEquals('u.username DESC', (string) $orderByExpr);
$this->assertEquals($orderExpr->count(), 1);
$this->assertEquals('u.username DESC', (string) $orderExpr);
}
public function testOrderByOrder()
{
$orderByExpr = $this->_expr->orderBy('u.username', 'DESC');
$this->assertEquals('u.username DESC', (string) $orderByExpr);
$orderExpr = $this->_expr->desc('u.username');
$this->assertEquals('u.username DESC', (string) $orderExpr);
}
public function testOrderByDefaultOrderIsAsc()
public function testOrderByAsc()
{
$orderByExpr = $this->_expr->orderBy('u.username');
$this->assertEquals('u.username ASC', (string) $orderByExpr);
$orderExpr = $this->_expr->asc('u.username');
$this->assertEquals('u.username ASC', (string) $orderExpr);
}
/**
* @expectedException Doctrine\Common\DoctrineException
* @expectedException \InvalidArgumentException
*/
public function testAddThrowsException()
{

View File

@ -281,6 +281,16 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username ASC');
}
public function testOrderByWithExpression()
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->orderBy($qb->expr()->asc('u.username'));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username ASC');
}
public function testAddOrderBy()
{
@ -335,7 +345,7 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
->where('u.id = :id');
$qb->setParameters(array('id' => 1));
$this->assertEquals(array('id' => 1, 'test' => 1), $qb->getParameters(array('test' => 1)));
$this->assertEquals(array('id' => 1), $qb->getParameters());
}
public function testGetParameter()
@ -382,7 +392,7 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
public function testComplexWhere()
{
$qb = $this->_em->createQueryBuilder();
$orExpr = $qb->expr()->orx();
$orExpr = $qb->expr()->orX();
$orExpr->add($qb->expr()->eq('u.id', ':uid3'));
$orExpr->add($qb->expr()->in('u.id', array(1)));
@ -392,6 +402,90 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id = :uid3) OR (u.id IN(1))');
}
public function testWhereInWithStringLiterals()
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where($qb->expr()->in('u.name', array('one', 'two', 'three')));
$this->assertValidQueryBuilder($qb, "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IN('one', 'two', 'three')");
$qb->where($qb->expr()->in('u.name', array("O'Reilly", "O'Neil", 'Smith')));
$this->assertValidQueryBuilder($qb, "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IN('O''Reilly', 'O''Neil', 'Smith')");
}
public function testWhereInWithObjectLiterals()
{
$qb = $this->_em->createQueryBuilder();
$expr = $this->_em->getExpressionBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where($expr->in('u.name', array($expr->literal('one'), $expr->literal('two'), $expr->literal('three'))));
$this->assertValidQueryBuilder($qb, "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IN('one', 'two', 'three')");
$qb->where($expr->in('u.name', array($expr->literal("O'Reilly"), $expr->literal("O'Neil"), $expr->literal('Smith'))));
$this->assertValidQueryBuilder($qb, "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IN('O''Reilly', 'O''Neil', 'Smith')");
}
public function testNegation()
{
$expr = $this->_em->getExpressionBuilder();
$orExpr = $expr->orX();
$orExpr->add($expr->eq('u.id', ':uid3'));
$orExpr->add($expr->not($expr->in('u.id', array(1))));
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where($orExpr);
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id = :uid3) OR (NOT(u.id IN(1)))');
}
public function testSomeAllAny()
{
$qb = $this->_em->createQueryBuilder();
$expr = $this->_em->getExpressionBuilder();
//$subquery = $qb->subquery('Doctrine\Tests\Models\CMS\CmsArticle', 'a')->select('a.id');
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where($expr->gt('u.id', $expr->all('select a.id from Doctrine\Tests\Models\CMS\CmsArticle a')));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ALL(select a.id from Doctrine\Tests\Models\CMS\CmsArticle a)');
}
public function testMultipleIsolatedQueryConstruction()
{
$qb = $this->_em->createQueryBuilder();
$expr = $this->_em->getExpressionBuilder();
$qb->select('u')->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$qb->where($expr->eq('u.name', ':name'));
$qb->setParameter('name', 'romanb');
$q1 = $qb->getQuery();
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :name', $q1->getDql());
$this->assertEquals(1, count($q1->getParameters()));
// add another condition and construct a second query
$qb->andWhere($expr->eq('u.id', ':id'));
$qb->setParameter('id', 42);
$q2 = $qb->getQuery();
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.name = :name) AND (u.id = :id)', $q2->getDql());
$this->assertTrue($q1 !== $q2); // two different, independent queries
$this->assertEquals(2, count($q2->getParameters()));
$this->assertEquals(1, count($q1->getParameters())); // $q1 unaffected
}
public function testGetEntityManager()
{

View File

@ -128,7 +128,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
if (isset($this->_usedModelSets['generic'])) {
$conn->executeUpdate('DELETE FROM date_time_model');
}
$this->_em->clear();
}