From 4adc289596ef640d5c8c605e2aea5674e6502b46 Mon Sep 17 00:00:00 2001 From: romanb Date: Tue, 9 Feb 2010 17:13:49 +0000 Subject: [PATCH] [2.0][DDC-284] Fixed. API polish and some convention over configuration simplifications for join columns and join tables. --- doctrine-mapping.xsd | 12 +- .../Common/Annotations/Annotation.php | 10 + lib/Doctrine/DBAL/Configuration.php | 2 - lib/Doctrine/DBAL/Connection.php | 1 - lib/Doctrine/DBAL/ConnectionException.php | 17 +- lib/Doctrine/DBAL/DriverManager.php | 1 - lib/Doctrine/ORM/AbstractQuery.php | 2 +- lib/Doctrine/ORM/EntityManager.php | 27 ++- .../Internal/Hydration/HydrationException.php | 2 +- .../ORM/Mapping/AssociationMapping.php | 12 +- .../ORM/Mapping/ClassMetadataInfo.php | 58 +---- .../Mapping/Driver/DoctrineAnnotations.php | 14 +- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 10 +- .../ORM/Mapping/Driver/YamlDriver.php | 6 - .../ORM/Mapping/ManyToManyMapping.php | 37 +++- lib/Doctrine/ORM/Mapping/OneToManyMapping.php | 10 +- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 14 +- lib/Doctrine/ORM/PersistentCollection.php | 3 +- .../Persisters/StandardEntityPersister.php | 4 +- lib/Doctrine/ORM/Query/Expr.php | 199 ++++++------------ lib/Doctrine/ORM/Query/Expr/Base.php | 2 +- lib/Doctrine/ORM/Query/Expr/Func.php | 4 +- lib/Doctrine/ORM/Query/Expr/Literal.php | 9 + lib/Doctrine/ORM/QueryBuilder.php | 156 +++++++------- .../Doctrine/Tests/Models/CMS/CmsAddress.php | 2 +- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 2 +- .../ManyToManyBasicAssociationTest.php | 2 +- .../ORM/Mapping/AnnotationDriverTest.php | 5 +- .../Tests/ORM/Mapping/ClassMetadataTest.php | 22 +- .../Doctrine.Tests.ORM.Mapping.User.dcm.xml | 10 +- tests/Doctrine/Tests/ORM/Query/ExprTest.php | 42 +--- tests/Doctrine/Tests/ORM/QueryBuilderTest.php | 98 ++++++++- .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- 33 files changed, 415 insertions(+), 382 deletions(-) create mode 100644 lib/Doctrine/ORM/Query/Expr/Literal.php diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 080d43e8a..c212c8671 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -113,11 +113,11 @@ - + - + @@ -167,7 +167,7 @@ - + @@ -187,7 +187,7 @@ - + @@ -213,7 +213,7 @@ - + @@ -235,7 +235,7 @@ - + diff --git a/lib/Doctrine/Common/Annotations/Annotation.php b/lib/Doctrine/Common/Annotations/Annotation.php index 757cd8c2f..ba7704be0 100644 --- a/lib/Doctrine/Common/Annotations/Annotation.php +++ b/lib/Doctrine/Common/Annotations/Annotation.php @@ -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)."'."); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Configuration.php b/lib/Doctrine/DBAL/Configuration.php index 1872b88a9..abfe438cb 100644 --- a/lib/Doctrine/DBAL/Configuration.php +++ b/lib/Doctrine/DBAL/Configuration.php @@ -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) { diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index e1f3151f5..fb238a617 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -22,7 +22,6 @@ namespace Doctrine\DBAL; use Doctrine\Common\EventManager, - Doctrine\Common\DoctrineException, Doctrine\DBAL\DBALException; /** diff --git a/lib/Doctrine/DBAL/ConnectionException.php b/lib/Doctrine/DBAL/ConnectionException.php index 8c7fe9580..9ed596327 100644 --- a/lib/Doctrine/DBAL/ConnectionException.php +++ b/lib/Doctrine/DBAL/ConnectionException.php @@ -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 _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. */ diff --git a/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php index 413430a4b..886b42dec 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php +++ b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -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() { diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index dc0f9611a..d9c506c8a 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -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); } /** diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 2b981f384..94a7d86b3 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -254,15 +254,6 @@ class ClassMetadataInfo */ public $columnNames = array(); - /** - * Whether to automatically OUTER JOIN subtypes when a basetype is queried. - * - * This does only apply to the JOINED inheritance mapping strategy. - * - * @var boolean - */ - //public $joinSubclasses = true; - /** * The discriminator value of this class. * @@ -270,7 +261,7 @@ class ClassMetadataInfo * where a discriminator column is used. * * @var mixed - * @see _discriminatorColumn + * @see discriminatorColumn */ public $discriminatorValue; @@ -281,7 +272,7 @@ class ClassMetadataInfo * where a discriminator column is used. * * @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. - * - * This options does only apply to the JOINED inheritance mapping strategy. - * - * @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 - * @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) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index 334021496..152a1719f 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -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 {} diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 16bb8d74c..9e625a29c 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -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)) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index f8b67f793..36a21f87a 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -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'])) { diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php index 9a868f9e4..3a576c1de 100644 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php @@ -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 ClassMetadata instance. * * @since 2.0 * @author Roman Borschel @@ -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 + ); } } } diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php index d2afdd14e..e6bf21339 100644 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php @@ -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 ClassMetadata instance. * * @author Roman Borschel * @author Giorgio Sironi @@ -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. diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 6a113ccde..32f96ab4a 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -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 ClassMetadata instance. * * @since 2.0 * @author Roman Borschel @@ -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() { diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 93e875c5c..d412d2e11 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -21,8 +21,7 @@ namespace Doctrine\ORM; -use Doctrine\Common\DoctrineException, - Doctrine\ORM\Mapping\AssociationMapping, +use Doctrine\ORM\Mapping\AssociationMapping, \Closure; /** diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 25ef7e607..e80af98b5 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -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 } diff --git a/lib/Doctrine/ORM/Query/Expr.php b/lib/Doctrine/ORM/Query/Expr.php index 8da76129e..11c20840f 100644 --- a/lib/Doctrine/ORM/Query/Expr.php +++ b/lib/Doctrine/ORM/Query/Expr.php @@ -31,155 +31,77 @@ namespace Doctrine\ORM\Query; * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel + * @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 = . 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 * . Example: * @@ -461,8 +384,8 @@ class Expr * When converted to string, it will generated a / . 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) { diff --git a/lib/Doctrine/ORM/Query/Expr/Base.php b/lib/Doctrine/ORM/Query/Expr/Base.php index 49d419890..904b69bbe 100644 --- a/lib/Doctrine/ORM/Query/Expr/Base.php +++ b/lib/Doctrine/ORM/Query/Expr/Base.php @@ -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."); } } diff --git a/lib/Doctrine/ORM/Query/Expr/Func.php b/lib/Doctrine/ORM/Query/Expr/Func.php index 21010badb..48b1a5b5e 100644 --- a/lib/Doctrine/ORM/Query/Expr/Func.php +++ b/lib/Doctrine/ORM/Query/Expr/Func.php @@ -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() diff --git a/lib/Doctrine/ORM/Query/Expr/Literal.php b/lib/Doctrine/ORM/Query/Expr/Literal.php new file mode 100644 index 000000000..c1dd5f783 --- /dev/null +++ b/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,9 @@ +QueryBuilder that uses the given EntityManager. * - * @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; - } + }*/ } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index 6fb057f38..746697d32 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -40,7 +40,7 @@ class CmsAddress /** * @OneToOne(targetEntity="CmsUser") - * @JoinColumn(name="user_id", referencedColumnName="id") +// * @JoinColumn(name="user_id", referencedColumnName="id") */ public $user; diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index 4d263615f..148fa6674 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -12,7 +12,7 @@ class CmsUser { /** * @Id @Column(type="integer") - * @GeneratedValue(strategy="AUTO") + * @GeneratedValue */ public $id; /** diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index 447428a04..bdabf6206 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -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)); diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index 436b2e824..b99c72986 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -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']); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 728d0554f..338ef42c8 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -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'); diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml index bc8374dbf..193227faf 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml @@ -20,10 +20,8 @@ + - - - @@ -33,6 +31,9 @@ + + + @@ -41,9 +42,6 @@ - - - diff --git a/tests/Doctrine/Tests/ORM/Query/ExprTest.php b/tests/Doctrine/Tests/ORM/Query/ExprTest.php index c3e4194ab..3b5d23d83 100644 --- a/tests/Doctrine/Tests/ORM/Query/ExprTest.php +++ b/tests/Doctrine/Tests/ORM/Query/ExprTest.php @@ -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() { diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index 6d40edaa7..d4a592e65 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -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() { diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 9760f8bd6..f206f39e2 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -128,7 +128,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase if (isset($this->_usedModelSets['generic'])) { $conn->executeUpdate('DELETE FROM date_time_model'); } - + $this->_em->clear(); }