Compare commits

..

74 Commits
2.3.3 ... 2.3.5

Author SHA1 Message Date
Benjamin Eberlei
2c31ec4809 Release 2.3.5 2014-02-08 17:28:24 +01:00
Benjamin Eberlei
47daa48e1f Update UPGRADE.md notes with BC mention. 2014-02-08 15:41:54 +01:00
Benjamin Eberlei
1a30e0a2e0 Merge branch 'DDC-2715' into 2.3 2013-10-29 09:25:52 +01:00
jan brunnert
550f25f0c0 Removed unnecessary is_object() check 2013-10-29 09:25:30 +01:00
jan brunnert
4863e996db When the OptimisticLockingException is generated with the static function lockFailedVersionMismatch and the passed parameters are DateTime instances, the exception could not be thrown because the DateTime object is not implicitly converted to a string. 2013-10-29 09:25:30 +01:00
Benjamin Eberlei
08e6363684 Merge branch 'DDC-2759' into 2.3 2013-10-26 11:18:16 +02:00
Benjamin Eberlei
4fdd1b9c2b [DDC-2759] Fix regression in ArrayHydrator introduced in DDC-1884 at SHA c7b4c9bf0f 2013-10-26 11:17:51 +02:00
Chris Collins
11f4c90d07 Added a failing test case for DDC-2759. 2013-10-26 11:17:50 +02:00
Benjamin Eberlei
66d8b43f69 Merge branch 'DDC-2608' into 2.3 2013-09-08 16:03:26 +02:00
Benjamin Eberlei
5ded61466b Fix merge conflict leftover 2013-09-08 16:03:15 +02:00
Benjamin Eberlei
8d3617a773 [DDC-2608][DDC-2662] Fix SequenceGenerator requiring "sequenceName" and now throw exception. Fix a bug in quoting the sequenceName. 2013-09-08 16:02:29 +02:00
Benjamin Eberlei
71d5f85ce0 Merge branch 'DDC-2660' into 2.3 2013-09-08 14:41:38 +02:00
Benjamin Eberlei
0e3abb4d79 [DDC-2660] Fix error with NativeSQL, ResultSetMappingBuilder and Associations as Primary Key. 2013-09-08 14:41:16 +02:00
Benjamin Eberlei
b1ba11c06f Merge branch 'DDC-2506' into 2.3 2013-08-20 10:02:51 +02:00
Guilherme Blanco
d349e37ce9 Fixed DDC-2506 by manually updating code. Closes PR #708. 2013-08-20 10:02:32 +02:00
Benjamin Eberlei
fd178d0e0d Merge branch 'DDC-2607' into 2.3 2013-08-20 09:53:46 +02:00
Benjamin Eberlei
6f1b8259d8 Fix variable name 2013-08-20 09:53:37 +02:00
Dustin Thomson
9e54ddca31 Modified executeInserts method in JoinedSubclassPersister to only check for the presence of columns in a composite primary key 2013-08-20 09:52:50 +02:00
Benjamin Eberlei
465bf0f411 Merge branch 'DDC-2579' into 2.3 2013-08-10 18:05:40 +02:00
Fabio B. Silva
794a6bea1b fix DDC-2579 2013-08-10 18:05:28 +02:00
Benjamin Eberlei
e4fdfb0b35 Merge branch 'DDC-2582' into 2.3 2013-08-10 17:50:03 +02:00
Guilherme Blanco
cba05708a9 CS fixes. 2013-08-10 17:49:50 +02:00
Guilherme Blanco
f1f5163394 Fixed DDC-1884. 2013-08-10 17:49:50 +02:00
Benjamin Eberlei
7816025077 Merge branch 'DDC-2548' into 2.3 2013-08-10 17:44:08 +02:00
Michaël Gallego
6fd4e8f470 Allow to have non-distinct queries 2013-08-10 17:43:54 +02:00
Benjamin Eberlei
beed62045f Merge branch 'DDC-2565' into 2.3 2013-08-10 17:28:26 +02:00
Austin Morris
f23656e468 convert PersistentCollection functional tests to unit tests 2013-08-10 17:28:07 +02:00
Austin Morris
76694239fa remove redundant require_once for TestInit.php 2013-08-10 17:28:07 +02:00
Austin Morris
6da540ac10 do not initialize coll on add() 2013-08-10 17:28:06 +02:00
Austin Morris
a0a66431ea Initialize coll when using Collection methods inside PersistentCollection 2013-08-10 17:28:06 +02:00
Austin Morris
8956cfcf2a PersistentCollection - initialize coll - create failing tests 2013-08-10 17:28:06 +02:00
Benjamin Eberlei
063280b0bd Merge branch 'DDC-2587' into 2.3 2013-08-10 16:25:54 +02:00
J. Bruni
9e67efa37b Updated EntityGeneratorTest::testEntityTypeAlias 2013-08-10 16:25:35 +02:00
J. Bruni
00f62dd6c3 Updated EntityGeneratorTest::testEntityTypeAlias 2013-08-10 16:25:35 +02:00
J Bruni
b880452ab1 Corrected PHP type for "decimal" mapping type
"Basic Mapping" documentation says:
"decimal: Type that maps a SQL DECIMAL to a PHP string."
2013-08-10 16:25:35 +02:00
Benjamin Eberlei
de59307b8e Merge branch 'DDC-2517' into 2.3 2013-06-30 10:43:27 +02:00
shulcsm
6c74d8ad33 Clear visitedCollections
Visited collections are cleared only in commit(). Commit clears up only if it actually has something to do. Processing large amounts of records without changing them cause visitedCollections to grow without any way of clearing.
2013-06-30 10:43:08 +02:00
Benjamin Eberlei
0633aa54c8 Merge branch 'DDC-2530' into 2.3 2013-06-25 19:36:05 +02:00
Benjamin Eberlei
db0c1fad78 [DDC-2350] Eager Collections are not marked as initialized, leading to multiple queries being executed. 2013-06-25 19:35:48 +02:00
Benjamin Eberlei
9787b27518 Merge branch 'DDC-2471' into 2.3 2013-05-26 09:27:03 +02:00
Alexander
4f83e7e6c4 [DDC-2471] Fix EQ/NEQ null handling of criteria 2013-05-26 09:26:52 +02:00
Benjamin Eberlei
425f375899 Bump dev version to 2.3.5 2013-05-11 09:51:12 +02:00
Benjamin Eberlei
a41b02c080 Release 2.3.4 2013-05-11 09:51:12 +02:00
Benjamin Eberlei
8b3c206bc1 Bump dependency to 2.3.4 2013-05-11 09:47:19 +02:00
Benjamin Eberlei
e0feccc2e4 Merge branch 'DDC-2280' into 2.3 2013-05-09 18:17:03 +02:00
Benjamin Eberlei
818d3d27fe [DDC-2280] length attribute in <id> was not converted. 2013-05-09 18:16:55 +02:00
Benjamin Eberlei
98d3847a11 Merge branch 'DDC-2387' into 2.3 2013-05-09 12:11:23 +02:00
Benjamin Eberlei
63918f214b [DDC-2387] Fix DatabaseDriver not working with combinations of composite/association keys. 2013-05-09 12:11:04 +02:00
Benjamin Eberlei
acf21246c7 Merge branch 'DDC-2437' into 2.3 2013-05-09 11:04:14 +02:00
Vladislav Vlastovskiy
795e4a4b6b Added test complex inner join with indexBy 2013-05-09 11:03:36 +02:00
Vladislav Vlastovskiy
946a22f2c7 Swapped places indexBy and condition in accordance with EBNF 2013-05-09 11:03:36 +02:00
Benjamin Eberlei
bdd7482b3f Merge branch 'DDC-2423' into 2.3 2013-05-09 10:56:00 +02:00
Benjamin Eberlei
8e31107f86 [DDC-2423] Fixed bug with EntityGenerator not generating fetch="" attribute in association annotations. 2013-05-09 10:55:42 +02:00
Benjamin Eberlei
209090d338 Merge branch 'DDC-2267' into 2.3 2013-05-04 13:39:37 +02:00
Benjamin Eberlei
27117bbc98 [DDC-2267] Allow EntityManager#flush($entity) to be called on entities scheduled for removal. 2013-05-04 13:39:23 +02:00
Benjamin Eberlei
0f2be50d8f [DDC-2426] Missing length attribute in doctrine-mapping.xsd for <id> tag. 2013-05-04 12:59:02 +02:00
Benjamin Eberlei
193e74af05 Merge branch 'DDC-1984' into 2.3 2013-05-01 19:40:06 +02:00
Benjamin Eberlei
bb6e4f2074 [DDC-1984] Throw exception if passing null into UnitOfWork#lock() - which can happen when EntityManager#find() tries to lock entity that was just deleted by another process. 2013-05-01 19:39:48 +02:00
Benjamin Eberlei
6063fe4adf Merge branch 'DDC-2409' into 2.3 2013-05-01 11:01:17 +02:00
Benjamin Eberlei
846c6d2c5e Simplify condition of previous commit (5cdc73e) 2013-05-01 11:00:55 +02:00
Fabio B. Silva
1f257622a1 Fix DDC-2409 2013-05-01 11:00:55 +02:00
Benjamin Eberlei
84257c9454 Merge branch 'DBAL-483' into 2.3 2013-05-01 10:45:19 +02:00
Benjamin Eberlei
eedacc9822 [DBAL-483] Add sqlite check again as ALTER TABLE is only supported as of 2.4 2013-05-01 10:44:31 +02:00
Benjamin Eberlei
3d9099e33b [DBAL-483] Pass default values to DBAL mapping layer correctly to fix default comparision bug. 2013-05-01 10:42:53 +02:00
EuKov
ad7b5871bf Fixed typo in SQLFilter (use statement ClassMetadata) 2013-04-23 22:26:59 +02:00
Benjamin Eberlei
803f2740c9 [DDC-2346] Reapply changes lost in rebase onto 2.3 2013-04-14 09:52:39 +02:00
Stefan Kleff
ed3f375ef3 Added constant 2013-04-14 09:49:26 +02:00
Stefan Kleff
a188c88ef2 Added test based on e468ced00b 2013-04-14 09:48:09 +02:00
Benjamin Eberlei
86e40a692b Merge branch 'DDC-2252' into 2.3 2013-04-06 19:56:15 +02:00
Fabio B. Silva
36b79309bd Fix DDC-2252 2013-04-06 19:55:50 +02:00
Benjamin Eberlei
66b6b7169e Merge branch 'DDC-2224-2' into 2.3 2013-04-04 20:35:01 +02:00
Benjamin Eberlei
7c7a8abe40 [DDC-2224] Adjust ClassMetadata processing 2013-04-04 20:34:49 +02:00
Benjamin Eberlei
e5c68ab45c [DDC-2224] Rewrite instanceof feature with parameter needle ClassMetadata breaks caching of queries. 2013-04-04 20:32:04 +02:00
Benjamin Eberlei
868bb68cc8 Bump dev version to 2.3.4 2013-03-24 21:43:58 +01:00
55 changed files with 1846 additions and 168 deletions

View File

@@ -1,5 +1,11 @@
# Upgrade to 2.3
## Auto Discriminator Map breaks userland implementations with Listener
The new feature to detect discriminator maps automatically when none
are provided breaks userland implementations doing this with a
listener in ``loadClassMetadata`` event.
## EntityManager#find() not calls EntityRepository#find() anymore
Previous to 2.3, calling ``EntityManager#find()`` would be delegated to

View File

@@ -1,6 +1,6 @@
{
"name": "doctrine/orm",
"type": "library","version":"2.3.3",
"type": "library","version":"2.3.5",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",

View File

@@ -329,6 +329,7 @@
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" />
<xs:attribute name="column" type="xs:NMTOKEN" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="association-key" type="xs:boolean" default="false" />
<xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/>

View File

@@ -26,6 +26,7 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Mapping;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -259,6 +260,9 @@ abstract class AbstractQuery
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
return $this->convertObjectParameterToScalarValue($value);
case ($value instanceof Mapping\ClassMetadata):
return $value->name;
default:
return $value;
}
@@ -658,6 +662,18 @@ abstract class AbstractQuery
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
}
/**
* Check if the query has a hint
*
* @param string $name The name of the hint
*
* @return bool False if the query does not have any hint
*/
public function hasHint($name)
{
return isset($this->_hints[$name]);
}
/**
* Return the key value map of query hints that are currently set.
*

View File

@@ -118,6 +118,7 @@ class ArrayHydrator extends AbstractHydrator
$baseElement =& $this->_resultPointers[$parent];
} else {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
continue;
}
@@ -139,6 +140,7 @@ class ArrayHydrator extends AbstractHydrator
if ( ! $indexExists || ! $indexIsValid) {
$element = $data;
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
} else {
@@ -155,7 +157,10 @@ class ArrayHydrator extends AbstractHydrator
} else {
$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
if (
( ! isset($nonemptyComponents[$dqlAlias])) &&
( ! isset($baseElement[$relationAlias]))
) {
$baseElement[$relationAlias] = null;
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = $data;
@@ -164,10 +169,9 @@ class ArrayHydrator extends AbstractHydrator
$coll =& $baseElement[$relationAlias];
if ($coll !== null) {
if (is_array($coll)) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
} else {
// It's a root result element
@@ -176,22 +180,21 @@ class ArrayHydrator extends AbstractHydrator
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array($entityKey => null);
} else {
$result[] = null;
}
$result[] = $this->_rsm->isMixed
? array($entityKey => null)
: null;
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
continue;
}
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias];
if ($this->_rsm->isMixed) {
$element = array($entityKey => $element);
}
$element = $this->_rsm->isMixed
? array($entityKey => $rowData[$dqlAlias])
: $rowData[$dqlAlias];
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
@@ -199,6 +202,7 @@ class ArrayHydrator extends AbstractHydrator
} else {
$resultKey = $this->_resultCounter;
$result[] = $element;
++$this->_resultCounter;
}
@@ -206,11 +210,13 @@ class ArrayHydrator extends AbstractHydrator
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$resultKey = $index;
/*if ($this->_rsm->isMixed) {
$result[] =& $result[$index];
++$this->_resultCounter;
}*/
}
$this->updateResultPointer($result, $index, $dqlAlias, false);
}
}
@@ -219,11 +225,9 @@ class ArrayHydrator extends AbstractHydrator
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
// this only ever happens when no object is fetched (scalar result only)
if (isset($this->_rsm->indexByMap['scalars'])) {
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
} else {
$resultKey = $this->_resultCounter - 1;
}
$resultKey = isset($this->_rsm->indexByMap['scalars'])
? $row[$this->_rsm->indexByMap['scalars']]
: $this->_resultCounter - 1;
}
foreach ($scalars as $name => $value) {
@@ -249,6 +253,12 @@ class ArrayHydrator extends AbstractHydrator
return;
}
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index];
@@ -259,12 +269,6 @@ class ArrayHydrator extends AbstractHydrator
return;
}
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
return;
}
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];

View File

@@ -27,6 +27,7 @@ use PDO,
Doctrine\ORM\Events,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection,
Doctrine\ORM\UnitOfWork,
Doctrine\ORM\Proxy\Proxy;
/**
@@ -65,8 +66,8 @@ class ObjectHydrator extends AbstractHydrator
$this->_resultCounter = 0;
if ( ! isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
$this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
@@ -123,7 +124,7 @@ class ObjectHydrator extends AbstractHydrator
*/
protected function cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
$eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
parent::cleanup();

View File

@@ -2606,8 +2606,12 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function setSequenceGeneratorDefinition(array $definition)
{
if (isset($definition['name']) && $definition['name'] == '`') {
$definition['name'] = trim($definition['name'], '`');
if ( ! isset($definition['sequenceName'])) {
throw MappingException::missingSequenceName($this->name);
}
if ($definition['sequenceName'][0] == '`') {
$definition['sequenceName'] = trim($definition['sequenceName'], '`');
$definition['quoted'] = true;
}

View File

@@ -217,7 +217,8 @@ class DatabaseDriver implements MappingDriver
}
if ($ids) {
if (count($ids) == 1) {
// We need to check for the columns here, because we might have associations as id as well.
if (count($primaryKeyColumns) == 1) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
}

View File

@@ -438,4 +438,16 @@ class MappingException extends \Doctrine\ORM\ORMException
$cascades
));
}
/**
* @param string $className
*
* @return MappingException
*/
public static function missingSequenceName($className)
{
return new self(
sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className)
);
}
}

View File

@@ -54,6 +54,8 @@ class OptimisticLockException extends ORMException
public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion)
{
$expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
$actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion;
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
}

View File

@@ -717,6 +717,8 @@ final class PersistentCollection implements Collection, Selectable
public function key()
{
$this->initialize();
return $this->coll->key();
}
@@ -725,6 +727,8 @@ final class PersistentCollection implements Collection, Selectable
*/
public function current()
{
$this->initialize();
return $this->coll->current();
}
@@ -733,6 +737,8 @@ final class PersistentCollection implements Collection, Selectable
*/
public function next()
{
$this->initialize();
return $this->coll->next();
}

View File

@@ -88,7 +88,7 @@ class BasicEntityPersister
*/
static private $comparisonMap = array(
Comparison::EQ => '= %s',
Comparison::IS => 'IS %s',
Comparison::IS => '= %s',
Comparison::NEQ => '!= %s',
Comparison::GT => '> %s',
Comparison::GTE => '>= %s',
@@ -518,13 +518,34 @@ class BasicEntityPersister
*/
public function delete($entity)
{
$class = $this->_class;
$em = $this->_em;
$identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$tableName = $this->quoteStrategy->getTableName($class, $this->_platform);
$idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->_platform);
$id = array_combine($idColumns, $identifier);
$types = array_map(function ($identifier) use ($class, $em) {
if (isset($class->fieldMappings[$identifier])) {
return $class->fieldMappings[$identifier]['type'];
}
$targetMapping = $em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']);
if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) {
return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
}
if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) {
return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
}
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
}, $class->identifier);
$this->deleteJoinTableRecords($identifier);
$id = array_combine($this->quoteStrategy->getIdentifierColumnNames($this->_class, $this->_platform), $identifier);
$this->_conn->delete($this->quoteStrategy->getTableName($this->_class, $this->_platform), $id);
$this->_conn->delete($tableName, $id, $types);
}
/**
@@ -790,7 +811,7 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
return $hydrator->hydrateAll($stmt, $this->_rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
}
/**
@@ -845,7 +866,7 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
return $hydrator->hydrateAll($stmt, $this->_rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
}
/**
@@ -874,7 +895,7 @@ class BasicEntityPersister
*/
private function loadArrayFromStatement($assoc, $stmt)
{
$hints = array('deferEagerLoads' => true);
$hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true);
if (isset($assoc['indexBy'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
@@ -899,7 +920,7 @@ class BasicEntityPersister
*/
private function loadCollectionFromStatement($assoc, $stmt, $coll)
{
$hints = array('deferEagerLoads' => true, 'collection' => $coll);
$hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true, 'collection' => $coll);
if (isset($assoc['indexBy'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
@@ -1457,6 +1478,18 @@ class BasicEntityPersister
$placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->_platform);
}
if ($comparison !== null) {
// special case null value handling
if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) {
return $conditionSql . ' IS NULL';
} else if ($comparison === Comparison::NEQ && $value === null) {
return $conditionSql . ' IS NOT NULL';
}
return $conditionSql . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
}
$conditionSql .= ($comparison === null)
? ((is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ' . $placeholder))
: ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);

View File

@@ -184,7 +184,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
if (!is_array($id) || !isset($id[$columnName])) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
}
}
$stmt->execute();

View File

@@ -186,22 +186,22 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/
protected function _getDeleteSQLParameters(PersistentCollection $coll)
{
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
$mapping = $coll->getMapping();
$params = array();
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
// Optimization for single column identifier
if (count($mapping['relationToSourceKeyColumns']) === 1) {
$params[] = array_pop($identifier);
return $params;
return array(reset($identifier));
}
// Composite identifier
$sourceClass = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$params = array();
foreach ($mapping['relationToSourceKeyColumns'] as $srcColumn) {
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
$params[] = isset($sourceClass->fieldNames[$refColumnName])
? $identifier[$sourceClass->fieldNames[$refColumnName]]
: $identifier[$sourceClass->getFieldForColumn($columnName)];
}
return $params;

View File

@@ -57,7 +57,14 @@ class SqlValueVisitor extends ExpressionVisitor
{
$value = $comparison->getValue()->getValue();
$field = $comparison->getField();
$operator = $comparison->getOperator();
if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) {
return;
} else if ($operator === Comparison::NEQ && $value === null) {
return;
}
$this->values[] = $value;
$this->types[] = array($field, $value);
}

View File

@@ -141,7 +141,7 @@ class Join
{
return strtoupper($this->joinType) . ' JOIN ' . $this->join
. ($this->alias ? ' ' . $this->alias : '')
. ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : '')
. ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '');
. ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '')
. ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : '');
}
}

View File

@@ -19,9 +19,9 @@
namespace Doctrine\ORM\Query\Filter;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetaData,
Doctrine\ORM\Query\ParameterTypeInferer;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ParameterTypeInferer;
/**
* The base class that user defined filters should extend.

View File

@@ -101,7 +101,13 @@ class ResultSetMappingBuilder extends ResultSetMapping
if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $renamedColumnName, $columnName);
$this->addMetaResult(
$alias,
$renamedColumnName,
$columnName,
(isset($associationMapping['id']) && $associationMapping['id'] === true)
);
}
}
}

View File

@@ -804,9 +804,13 @@ class SqlWalker implements TreeWalker
/**
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
*
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
* @param int $joinType
* @param AST\ConditionalExpression $condExpr
*
* @return string
*/
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER)
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null)
{
$sql = '';
@@ -921,6 +925,13 @@ class SqlWalker implements TreeWalker
break;
}
// Handle WITH clause
if ($condExpr !== null) {
// Phase 2 AST optimization: Skip processing of ConditionalExpression
// if only one ConditionalTerm is defined
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
}
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
if ($targetClass->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
@@ -1020,14 +1031,7 @@ class SqlWalker implements TreeWalker
break;
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
// Handle WITH clause
if (($condExpr = $join->conditionalExpression) !== null) {
// Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
}
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
break;
}
@@ -1916,31 +1920,22 @@ class SqlWalker implements TreeWalker
foreach ($instanceOfExpr->value as $parameter) {
if ($parameter instanceof AST\InputParameter) {
// We need to modify the parameter value to be its correspondent mapped value
$dqlParamKey = $parameter->name;
$dqlParam = $this->query->getParameter($dqlParamKey);
$paramValue = $this->query->processParameterValue($dqlParam->getValue());
if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) {
throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue));
}
$entityClassName = $paramValue->name;
$sqlParameterList[] = $this->walkInputParameter($parameter);
} else {
// Get name from ClassMetadata to resolve aliases.
$entityClassName = $this->em->getClassMetadata($parameter)->name;
}
if ($entityClassName == $class->name) {
$sqlParameterList[] = $this->conn->quote($class->discriminatorValue);
} else {
$discrMap = array_flip($class->discriminatorMap);
if ($entityClassName == $class->name) {
$sqlParameterList[] = $this->conn->quote($class->discriminatorValue);
} else {
$discrMap = array_flip($class->discriminatorMap);
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]);
}
$sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]);
}
}

View File

@@ -152,7 +152,7 @@ class EntityGenerator
Type::SMALLINT => 'integer',
Type::TEXT => 'string',
Type::BLOB => 'string',
Type::DECIMAL => 'float',
Type::DECIMAL => 'string',
Type::JSON_ARRAY => 'array',
Type::SIMPLE_ARRAY => 'array',
);
@@ -1089,6 +1089,15 @@ public function __construct()
$typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
}
if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) {
$fetchMap = array(
ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY',
ClassMetadataInfo::FETCH_EAGER => 'EAGER',
);
$typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"';
}
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {

View File

@@ -140,9 +140,15 @@ class XmlExporter extends AbstractExporter
if (isset($field['columnName'])) {
$idXml->addAttribute('column', $field['columnName']);
}
if (isset($field['length'])) {
$idXml->addAttribute('length', $field['length']);
}
if (isset($field['associationKey']) && $field['associationKey']) {
$idXml->addAttribute('association-key', 'true');
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$generatorXml = $idXml->addChild('generator');
$generatorXml->addAttribute('strategy', $idGeneratorType);

View File

@@ -122,7 +122,7 @@ class Paginator implements \Countable, \IteratorAggregate
/* @var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
if ( ! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}

View File

@@ -385,22 +385,14 @@ class SchemaTool
}
if (isset($mapping['options'])) {
if (isset($mapping['options']['comment'])) {
$options['comment'] = $mapping['options']['comment'];
$knownOptions = array('comment', 'unsigned', 'fixed', 'default');
unset($mapping['options']['comment']);
}
foreach ($knownOptions as $knownOption) {
if ( isset($mapping['options'][$knownOption])) {
$options[$knownOption] = $mapping['options'][$knownOption];
if (isset($mapping['options']['unsigned'])) {
$options['unsigned'] = $mapping['options']['unsigned'];
unset($mapping['options']['unsigned']);
}
if (isset($mapping['options']['fixed'])) {
$options['fixed'] = $mapping['options']['fixed'];
unset($mapping['options']['fixed']);
unset($mapping['options'][$knownOption]);
}
}
$options['customSchemaOptions'] = $mapping['options'];

View File

@@ -67,6 +67,13 @@ class UnitOfWork implements PropertyChangedListener
*/
const STATE_REMOVED = 4;
/**
* Hint used to collect all primary keys of associated entities during hydration
* and execute it in a dedicated query afterwards
* @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql
*/
const HINT_DEFEREAGERLOAD = 'deferEagerLoad';
/**
* The identity map that holds references to all managed entities that have
* an identity. The entities are grouped by their class name.
@@ -400,13 +407,15 @@ class UnitOfWork implements PropertyChangedListener
*/
private function computeSingleEntityChangeSet($entity)
{
if ( $this->getEntityState($entity) !== self::STATE_MANAGED) {
throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity));
$state = $this->getEntityState($entity);
if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
}
$class = $this->em->getClassMetadata(get_class($entity));
if ($class->isChangeTrackingDeferredImplicit()) {
if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
$this->persist($entity);
}
@@ -1824,7 +1833,7 @@ class UnitOfWork implements PropertyChangedListener
// do not merge fields marked lazy that have not been fetched.
continue;
} else if ( ! $assoc2['isCascadeMerge']) {
if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) {
if ($this->getEntityState($other) === self::STATE_DETACHED) {
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
$relatedId = $targetClass->getIdentifierValues($other);
@@ -1835,6 +1844,7 @@ class UnitOfWork implements PropertyChangedListener
$this->registerManaged($other, $relatedId, array());
}
}
$prop->setValue($managedCopy, $other);
}
} else {
@@ -2226,6 +2236,10 @@ class UnitOfWork implements PropertyChangedListener
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
if ($entity === null) {
throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
}
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
throw ORMInvalidArgumentException::entityNotManaged($entity);
}
@@ -2304,6 +2318,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->visitedCollections =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
@@ -2552,7 +2567,7 @@ class UnitOfWork implements PropertyChangedListener
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
// then we can append this entity for eager loading!
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
isset($hints['deferEagerLoad']) &&
isset($hints[self::HINT_DEFEREAGERLOAD]) &&
!$targetClass->isIdentifierComposite &&
$newValue instanceof Proxy &&
$newValue->__isInitialized__ === false) {
@@ -2577,7 +2592,7 @@ class UnitOfWork implements PropertyChangedListener
break;
// Deferred eager load only works for single identifier classes
case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite):
case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite):
// TODO: Is there a faster approach?
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
@@ -2694,6 +2709,8 @@ class UnitOfWork implements PropertyChangedListener
$persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
break;
}
$collection->setInitialized(true);
}
/**

View File

@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.3.3';
const VERSION = '2.3.5';
/**
* Compares a Doctrine version with the current one.

View File

@@ -0,0 +1,42 @@
<?php
namespace Doctrine\Tests\Models\Taxi;
/**
* @Entity
* @Table(name="taxi_car")
*/
class Car
{
/**
* @Id
* @Column(type="string", length=25)
* @GeneratedValue(strategy="NONE")
*/
private $brand;
/**
* @Column(type="string", length=255);
*/
private $model;
/**
* @OneToMany(targetEntity="Ride", mappedBy="car")
*/
private $freeCarRides;
/**
* @OneToMany(targetEntity="PaidRide", mappedBy="car")
*/
private $carRides;
public function setBrand($brand)
{
$this->brand = $brand;
}
public function setModel($model)
{
$this->model = $model;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Tests\Models\Taxi;
/**
* @Entity
* @Table(name="taxi_driver")
*/
class Driver
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Column(type="string", length=255);
*/
private $name;
/**
* @OneToMany(targetEntity="Ride", mappedBy="driver")
*/
private $freeDriverRides;
/**
* @OneToMany(targetEntity="PaidRide", mappedBy="driver")
*/
private $driverRides;
public function setName($name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Doctrine\Tests\Models\Taxi;
/**
* Same as Ride but with an extra column that is not part of the composite primary key
*
* @Entity
* @Table(name="taxi_paid_ride")
*/
class PaidRide
{
/**
* @Id
* @ManyToOne(targetEntity="Driver", inversedBy="driverRides")
* @JoinColumn(name="driver_id", referencedColumnName="id")
*/
private $driver;
/**
* @Id
* @ManyToOne(targetEntity="Car", inversedBy="carRides")
* @JoinColumn(name="car", referencedColumnName="brand")
*/
private $car;
/**
* @Column(type="decimal", precision=6, scale=2)
*/
private $fare;
public function __construct(Driver $driver, Car $car)
{
$this->driver = $driver;
$this->car = $car;
}
public function setFare($fare)
{
$this->fare = $fare;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Doctrine\Tests\Models\Taxi;
/**
* Test model that contains only Id-columns
*
* @Entity
* @Table(name="taxi_ride")
*/
class Ride
{
/**
* @Id
* @ManyToOne(targetEntity="Driver", inversedBy="freeDriverRides")
* @JoinColumn(name="driver_id", referencedColumnName="id")
*/
private $driver;
/**
* @Id
* @ManyToOne(targetEntity="Car", inversedBy="freeCarRides")
* @JoinColumn(name="car", referencedColumnName="brand")
*/
private $car;
public function __construct(Driver $driver, Car $car)
{
$this->driver = $driver;
$this->car = $car;
}
}

View File

@@ -1118,7 +1118,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->username = 'domnikl';
$user->status = 'developer';
$this->setExpectedException('InvalidArgumentException', 'Entity has to be managed for single computation');
$this->setExpectedException('InvalidArgumentException', 'Entity has to be managed or scheduled for removal for single computation');
$this->_em->flush($user);
}
@@ -1202,8 +1202,9 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
/**
* @group DDC-720
* @group DDC-1612
* @group DDC-2267
*/
public function testFlushSingleNewEntity()
public function testFlushSingleNewEntityThenRemove()
{
$user = new CmsUser;
$user->name = 'Dominik';
@@ -1212,6 +1213,14 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($user);
$this->_em->flush($user);
$userId = $user->id;
$this->_em->remove($user);
$this->_em->flush($user);
$this->_em->clear();
$this->assertNull($this->_em->find(get_class($user), $userId));
}
/**

View File

@@ -2,12 +2,9 @@
namespace Doctrine\Tests\ORM\Functional;
require_once __DIR__ . '/../../TestInit.php';
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\Common\Util\Inflector;
class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
class DatabaseDriverTest extends DatabaseDriverTestCase
{
/**
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager
@@ -148,44 +145,4 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), "no association mappings should be detected.");
}
protected function convertToClassMetadata(array $entityTables, array $manyTables = array())
{
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm);
$driver->setTables($entityTables, $manyTables);
$metadatas = array();
foreach ($driver->getAllClassNames() AS $className) {
$class = new ClassMetadataInfo($className);
$driver->loadMetadataForClass($className, $class);
$metadatas[$className] = $class;
}
return $metadatas;
}
/**
* @param string $className
* @return ClassMetadata
*/
protected function extractClassMetadata(array $classNames)
{
$classNames = array_map('strtolower', $classNames);
$metadatas = array();
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm);
foreach ($driver->getAllClassNames() as $className) {
if (!in_array(strtolower($className), $classNames)) {
continue;
}
$class = new ClassMetadataInfo($className);
$driver->loadMetadataForClass($className, $class);
$metadatas[$className] = $class;
}
if (count($metadatas) != count($classNames)) {
$this->fail("Have not found all classes matching the names '" . implode(", ", $classNames) . "' only tables " . implode(", ", array_keys($metadatas)));
}
return $metadatas;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Common BaseClass for DatabaseDriver Tests
*/
abstract class DatabaseDriverTestCase extends OrmFunctionalTestCase
{
protected function convertToClassMetadata(array $entityTables, array $manyTables = array())
{
$sm = $this->_em->getConnection()->getSchemaManager();
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($sm);
$driver->setTables($entityTables, $manyTables);
$metadatas = array();
foreach ($driver->getAllClassNames() AS $className) {
$class = new ClassMetadataInfo($className);
$driver->loadMetadataForClass($className, $class);
$metadatas[$className] = $class;
}
return $metadatas;
}
/**
* @param string $className
* @return ClassMetadata
*/
protected function extractClassMetadata(array $classNames)
{
$classNames = array_map('strtolower', $classNames);
$metadatas = array();
$sm = $this->_em->getConnection()->getSchemaManager();
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($sm);
foreach ($driver->getAllClassNames() as $className) {
if (!in_array(strtolower($className), $classNames)) {
continue;
}
$class = new ClassMetadataInfo($className);
$driver->loadMetadataForClass($className, $class);
$metadatas[$className] = $class;
}
if (count($metadatas) != count($classNames)) {
$this->fail("Have not found all classes matching the names '" . implode(", ", $classNames) . "' only tables " . implode(", ", array_keys($metadatas)));
}
return $metadatas;
}
}

View File

@@ -84,4 +84,60 @@ class EntityRepositoryCriteriaTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(2, count($dates));
}
private function loadNullFieldFixtures()
{
$today = new DateTimeModel();
$today->datetime =
$today->date =
new \DateTime('today');
$this->_em->persist($today);
$tomorrow = new DateTimeModel();
$tomorrow->datetime =
$tomorrow->date =
$tomorrow->time =
new \DateTime('tomorrow');
$this->_em->persist($tomorrow);
$this->_em->flush();
$this->_em->clear();
}
public function testIsNullComparison()
{
$this->loadNullFieldFixtures();
$repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel');
$dates = $repository->matching(new Criteria(
Criteria::expr()->isNull('time')
));
$this->assertEquals(1, count($dates));
}
public function testEqNullComparison()
{
$this->loadNullFieldFixtures();
$repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel');
$dates = $repository->matching(new Criteria(
Criteria::expr()->eq('time', null)
));
$this->assertEquals(1, count($dates));
}
public function testNotEqNullComparison()
{
$this->loadNullFieldFixtures();
$repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel');
$dates = $repository->matching(new Criteria(
Criteria::expr()->neq('time', null)
));
$this->assertEquals(1, count($dates));
}
}

View File

@@ -7,6 +7,8 @@ use Doctrine\ORM\OptimisticLockException;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\Tests\TestUtil;
use Doctrine\DBAL\LockMode;
use DateTime;
require_once __DIR__ . '/../../../TestInit.php';
@@ -181,13 +183,44 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', array(date($format, strtotime($test->version->format($format)) + 3600), $test->id));
// Try and update the record and it should throw an exception
$caughtException = null;
$test->name = 'Testing again';
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
$caughtException = $e;
}
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
$this->assertSame($test, $caughtException->getEntity());
}
/**
* @depends testOptimisticTimestampSetsDefaultValue
*/
public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
$q->setParameter('id', $entity->id);
$test = $q->getSingleResult();
$this->assertInstanceOf('DateTime', $test->version);
// Try to lock the record with an older timestamp and it should throw an exception
$caughtException = null;
try {
$expectedVersionExpired = DateTime::createFromFormat('U', $test->version->getTimestamp()-3600);
$this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired);
} catch (OptimisticLockException $e) {
$caughtException = $e;
}
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
$this->assertSame($test, $caughtException->getEntity());
}
}
/**

View File

@@ -0,0 +1,60 @@
<?php
namespace Doctrine\Tests\ORM\Functional\SchemaTool;
use Doctrine\ORM\Tools;
class DBAL483Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$conn = $this->_em->getConnection();
if ($conn->getDatabasePlatform()->getName() === 'sqlite') {
$this->markTestSkipped('Sqlite does not support ALTER TABLE');
}
$this->schemaTool = new Tools\SchemaTool($this->_em);
}
/**
* @group DBAL-483
*/
public function testDefaultValueIsComparedCorrectly()
{
$class = $this->_em->getClassMetadata(__NAMESPACE__ . '\\DBAL483Default');
$this->schemaTool->createSchema(array($class));
$updateSql = $this->schemaTool->getUpdateSchemaSql(array($class));
$updateSql = array_filter($updateSql, function ($sql) {
return strpos($sql, 'DBAL483') !== false;
});
$this->assertEquals(0, count($updateSql));
}
}
/**
* @Entity
*/
class DBAL483Default
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
/**
* @Column(type="integer", options={"default": 0})
*/
public $num;
/**
* @Column(type="string", options={"default": "foo"})
*/
public $str = "foo";
}

View File

@@ -4,9 +4,6 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool;
use Doctrine\ORM\Tools;
require_once __DIR__ . '/../../../TestInit.php';
/**
* WARNING: This test should be run as last test! It can affect others very easily!
*/
@@ -15,7 +12,8 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase
private $classes = array();
private $schemaTool = null;
public function setUp() {
public function setUp()
{
parent::setUp();
$conn = $this->_em->getConnection();
@@ -88,4 +86,4 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0, count($sql), "SQL: " . implode(PHP_EOL, $sql));
}
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\Taxi\Car,
Doctrine\Tests\Models\Taxi\Driver,
Doctrine\Tests\Models\Taxi\Ride,
Doctrine\Tests\Models\Taxi\PaidRide;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1884
* @author Sander Coolen <sander@jibber.nl>
*/
class DDC1884Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('taxi');
parent::setUp();
list($bimmer, $crysler, $merc, $volvo) = $this->createCars('Doctrine\Tests\Models\Taxi\Car');
list($john, $foo) = $this->createDrivers('Doctrine\Tests\Models\Taxi\Driver');
$this->_em->flush();
$ride1 = new Ride($john, $bimmer);
$ride2 = new Ride($john, $merc);
$ride3 = new Ride($john, $volvo);
$ride4 = new Ride($foo, $merc);
$this->_em->persist($ride1);
$this->_em->persist($ride2);
$this->_em->persist($ride3);
$this->_em->persist($ride4);
$ride5 = new PaidRide($john, $bimmer);
$ride5->setFare(10.50);
$ride6 = new PaidRide($john, $merc);
$ride6->setFare(16.00);
$ride7 = new PaidRide($john, $volvo);
$ride7->setFare(20.70);
$ride8 = new PaidRide($foo, $merc);
$ride8->setFare(32.15);
$this->_em->persist($ride5);
$this->_em->persist($ride6);
$this->_em->persist($ride7);
$this->_em->persist($ride8);
$this->_em->flush();
}
private function createCars($class)
{
$bimmer = new $class;
$bimmer->setBrand('BMW');
$bimmer->setModel('7-Series');
$crysler = new $class;
$crysler->setBrand('Crysler');
$crysler->setModel('300');
$merc = new $class;
$merc->setBrand('Mercedes');
$merc->setModel('C-Class');
$volvo = new $class;
$volvo->setBrand('Volvo');
$volvo->setModel('XC90');
$this->_em->persist($bimmer);
$this->_em->persist($crysler);
$this->_em->persist($merc);
$this->_em->persist($volvo);
return array($bimmer, $crysler, $merc, $volvo);
}
private function createDrivers($class)
{
$john = new $class;
$john->setName('John Doe');
$foo = new $class;
$foo->setName('Foo Bar');
$this->_em->persist($foo);
$this->_em->persist($john);
return array($john, $foo);
}
/**
* 1) Ride contains only columns that are part of its composite primary key
* 2) We use fetch joins here
*/
public function testSelectFromInverseSideWithCompositePkAndSolelyIdentifierColumnsUsingFetchJoins()
{
$qb = $this->_em->createQueryBuilder();
$result = $qb->select('d, dr, c')
->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
->leftJoin('d.freeDriverRides', 'dr')
->leftJoin('dr.car', 'c')
->where('d.name = ?1')
->setParameter(1, 'John Doe')
->getQuery()
->getArrayResult();
$this->assertCount(1, $result);
$this->assertArrayHasKey('freeDriverRides', $result[0]);
$this->assertCount(3, $result[0]['freeDriverRides']);
}
/**
* 1) PaidRide contains an extra column that is not part of the composite primary key
* 2) Again we will use fetch joins
*/
public function testSelectFromInverseSideWithCompositePkUsingFetchJoins()
{
$qb = $this->_em->createQueryBuilder();
$result = $qb->select('d, dr, c')
->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
->leftJoin('d.driverRides', 'dr')
->leftJoin('dr.car', 'c')
->where('d.name = ?1')
->setParameter(1, 'John Doe')
->getQuery()->getArrayResult();
$this->assertCount(1, $result);
$this->assertArrayHasKey('driverRides', $result[0]);
$this->assertCount(3, $result[0]['driverRides']);
}
/**
* The other way around will fail too
*/
public function testSelectFromOwningSideUsingFetchJoins()
{
$qb = $this->_em->createQueryBuilder();
$result = $qb->select('r, d, c')
->from('Doctrine\Tests\Models\Taxi\PaidRide', 'r')
->leftJoin('r.driver', 'd')
->leftJoin('r.car', 'c')
->where('d.name = ?1')
->setParameter(1, 'John Doe')
->getQuery()->getArrayResult();
$this->assertCount(3, $result);
$this->assertArrayHasKey('driver', $result[0]);
$this->assertArrayHasKey('car', $result[0]);
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @group DDC-2252
*/
class DDC2252Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $user;
private $merchant;
private $membership;
private $privileges = array();
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Ticket\DDC2252User'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Ticket\DDC2252Privilege'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Ticket\DDC2252Membership'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Ticket\DDC2252MerchantAccount'),
));
$this->loadFixtures();
}
public function loadFixtures()
{
$this->user = new DDC2252User;
$this->merchant = new DDC2252MerchantAccount;
$this->membership = new DDC2252Membership($this->user, $this->merchant);
$this->privileges[] = new DDC2252Privilege;
$this->privileges[] = new DDC2252Privilege;
$this->privileges[] = new DDC2252Privilege;
$this->membership->addPrivilege($this->privileges[0]);
$this->membership->addPrivilege($this->privileges[1]);
$this->membership->addPrivilege($this->privileges[2]);
$this->_em->persist($this->user);
$this->_em->persist($this->merchant);
$this->_em->persist($this->privileges[0]);
$this->_em->persist($this->privileges[1]);
$this->_em->persist($this->privileges[2]);
$this->_em->flush();
$this->_em->persist($this->membership);
$this->_em->flush();
$this->_em->clear();
}
public function testIssue()
{
$identifier = array(
'merchantAccount' => $this->merchant->getAccountid(),
'userAccount' => $this->user->getUid(),
);
$class = 'Doctrine\Tests\ORM\Functional\Ticket\DDC2252Membership';
$membership = $this->_em->find($class, $identifier);
$this->assertInstanceOf($class, $membership);
$this->assertCount(3, $membership->getPrivileges());
$membership->getPrivileges()->remove(2);
$this->_em->persist($membership);
$this->_em->flush();
$this->_em->clear();
$membership = $this->_em->find($class, $identifier);
$this->assertInstanceOf($class, $membership);
$this->assertCount(2, $membership->getPrivileges());
$membership->getPrivileges()->clear();
$this->_em->persist($membership);
$this->_em->flush();
$this->_em->clear();
$membership = $this->_em->find($class, $identifier);
$this->assertInstanceOf($class, $membership);
$this->assertCount(0, $membership->getPrivileges());
$membership->addPrivilege($privilege3 = new DDC2252Privilege);
$this->_em->persist($privilege3);
$this->_em->persist($membership);
$this->_em->flush();
$this->_em->clear();
$membership = $this->_em->find($class, $identifier);
$this->assertInstanceOf($class, $membership);
$this->assertCount(1, $membership->getPrivileges());
}
}
/**
* @Entity()
* @Table(name="ddc2252_acl_privilege")
*/
class DDC2252Privilege
{
/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
protected $privilegeid;
public function getPrivilegeid()
{
return $this->privilegeid;
}
}
/**
* @Entity
* @Table(name="ddc2252_mch_account")
*/
class DDC2252MerchantAccount
{
/**
* @Id
* @Column(type="integer")
*/
protected $accountid = 111;
public function getAccountid()
{
return $this->accountid;
}
}
/**
* @Entity
* @Table(name="ddc2252_user_account")
*/
class DDC2252User {
/**
* @Id
* @Column(type="integer")
*/
protected $uid = 222;
/**
* @OneToMany(targetEntity="DDC2252Membership", mappedBy="userAccount", cascade={"persist"})
* @JoinColumn(name="uid", referencedColumnName="uid")
*/
protected $memberships;
public function __construct()
{
$this->memberships = new ArrayCollection;
}
public function getUid()
{
return $this->uid;
}
public function getMemberships()
{
return $this->memberships;
}
public function addMembership(DDC2252Membership $membership)
{
$this->memberships[] = $membership;
}
}
/**
* @Entity
* @Table(name="ddc2252_mch_account_member")
* @HasLifecycleCallbacks
*/
class DDC2252Membership
{
/**
* @Id
* @ManyToOne(targetEntity="DDC2252User", inversedBy="memberships")
* @JoinColumn(name="uid", referencedColumnName="uid")
*/
protected $userAccount;
/**
* @Id
* @ManyToOne(targetEntity="DDC2252MerchantAccount")
* @JoinColumn(name="mch_accountid", referencedColumnName="accountid")
*/
protected $merchantAccount;
/**
* @ManyToMany(targetEntity="DDC2252Privilege", indexBy="privilegeid")
* @JoinTable(name="ddc2252_user_mch_account_privilege",
* joinColumns={
* @JoinColumn(name="mch_accountid", referencedColumnName="mch_accountid"),
* @JoinColumn(name="uid", referencedColumnName="uid")
* },
* inverseJoinColumns={
* @JoinColumn(name="privilegeid", referencedColumnName="privilegeid")
* }
* )
*/
protected $privileges;
public function __construct(DDC2252User $user, DDC2252MerchantAccount $merchantAccount)
{
$this->userAccount = $user;
$this->merchantAccount = $merchantAccount;
$this->privileges = new ArrayCollection();
}
public function addPrivilege($privilege)
{
$this->privileges[] = $privilege;
}
public function getPrivileges()
{
return $this->privileges;
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Logging\DebugStack;
/**
* @group DDC-2346
*/
class DDC2346Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var \Doctrine\DBAL\Logging\DebugStack
*/
protected $logger;
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Bar'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Baz'),
));
$this->logger = new DebugStack();
}
/**
* Verifies that fetching a OneToMany association with fetch="EAGER" does not cause N+1 queries
*/
public function testIssue()
{
$foo1 = new DDC2346Foo();
$foo2 = new DDC2346Foo();
$baz1 = new DDC2346Baz();
$baz2 = new DDC2346Baz();
$baz1->foo = $foo1;
$baz2->foo = $foo2;
$foo1->bars[] = $baz1;
$foo1->bars[] = $baz2;
$this->_em->persist($foo1);
$this->_em->persist($foo2);
$this->_em->persist($baz1);
$this->_em->persist($baz2);
$this->_em->flush();
$this->_em->clear();
$this->_em->getConnection()->getConfiguration()->setSQLLogger($this->logger);
$fetchedBazs = $this->_em->getRepository(__NAMESPACE__ . '\\DDC2346Baz')->findAll();
$this->assertCount(2, $fetchedBazs);
$this->assertCount(2, $this->logger->queries, 'The total number of executed queries is 2, and not n+1');
}
}
/** @Entity */
class DDC2346Foo
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/**
* @var DDC2346Bar[]|\Doctrine\Common\Collections\Collection
*
* @OneToMany(targetEntity="DDC2346Bar", mappedBy="foo")
*/
public $bars;
/** Constructor */
public function __construct() {
$this->bars = new ArrayCollection();
}
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"baz" = "DDC2346Baz"})
*/
class DDC2346Bar
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2346Foo", inversedBy="bars", fetch="EAGER") */
public $foo;
}
/**
* @Entity
*/
class DDC2346Baz extends DDC2346Bar
{
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group DDC-2350
*/
class DDC2350Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350Bug'),
));
}
public function testEagerCollectionsAreOnlyRetrievedOnce()
{
$user = new DDC2350User();
$bug1 = new DDC2350Bug();
$bug1->user = $user;
$bug2 = new DDC2350Bug();
$bug2->user = $user;
$this->_em->persist($user);
$this->_em->persist($bug1);
$this->_em->persist($bug2);
$this->_em->flush();
$this->_em->clear();
$cnt = $this->getCurrentQueryCount();
$user = $this->_em->find(__NAMESPACE__ . '\DDC2350User', $user->id);
$this->assertEquals($cnt + 2, $this->getCurrentQueryCount());
$this->assertEquals(2, count($user->reportedBugs));
$this->assertEquals($cnt + 2, $this->getCurrentQueryCount());
}
}
/**
* @Entity
*/
class DDC2350User
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToMany(targetEntity="DDC2350Bug", mappedBy="user", fetch="EAGER") */
public $reportedBugs;
}
/**
* @Entity
*/
class DDC2350Bug
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2350User", inversedBy="reportedBugs") */
public $user;
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\ORM\Functional\DatabaseDriverTestCase;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
class DDC2387Test extends DatabaseDriverTestCase
{
/**
* @group DDC-2387
*/
public function testCompositeAssociationKeyDetection()
{
$product = new \Doctrine\DBAL\Schema\Table('ddc2387_product');
$product->addColumn('id', 'integer');
$product->setPrimaryKey(array('id'));
$attributes = new \Doctrine\DBAL\Schema\Table('ddc2387_attributes');
$attributes->addColumn('product_id', 'integer');
$attributes->addColumn('attribute_name', 'string');
$attributes->setPrimaryKey(array('product_id', 'attribute_name'));
$attributes->addForeignKeyConstraint('ddc2387_product', array('product_id'), array('product_id'));
$metadata = $this->convertToClassMetadata(array($product, $attributes), array());
$this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_NONE, $metadata['Ddc2387Attributes']->generatorType);
$this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_AUTO, $metadata['Ddc2387Product']->generatorType);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsArticle;
/**
* @group DDC-2409
*/
class DDC2409Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$em = $this->_em;
$uow = $em->getUnitOfWork();
$originalArticle = new CmsArticle();
$originalUser = new CmsUser();
$originalArticle->topic = 'Unit Test';
$originalArticle->text = 'How to write a test';
$originalUser->name = 'Doctrine Bot';
$originalUser->username = 'DoctrineBot';
$originalUser->status = 'active';
$originalUser->addArticle($originalArticle);
$em->persist($originalUser);
$em->persist($originalArticle);
$em->flush();
$em->clear();
$article = $em->find('Doctrine\Tests\Models\CMS\CmsArticle', $originalArticle->id);
$user = new CmsUser();
$user->name = 'Doctrine Bot 2.0';
$user->username = 'BotDoctrine2';
$user->status = 'new';
$article->setAuthor($user);
$this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalArticle));
$this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalUser));
$this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($article));
$this->assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user));
$em->detach($user);
$em->detach($article);
$userMerged = $em->merge($user);
$articleMerged = $em->merge($article);
$this->assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user));
$this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($article));
$this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($userMerged));
$this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($articleMerged));
$this->assertNotSame($user, $userMerged);
$this->assertNotSame($article, $articleMerged);
$this->assertNotSame($userMerged, $articleMerged->user);
$this->assertSame($user, $articleMerged->user);
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* @group DDC-2579
*/
class DDC2579Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
Type::addType(DDC2579Type::NAME, DDC2579Type::CLASSNAME);
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(DDC2579Entity::CLASSNAME),
$this->_em->getClassMetadata(DDC2579EntityAssoc::CLASSNAME),
$this->_em->getClassMetadata(DDC2579AssocAssoc::CLASSNAME),
));
}
public function testIssue()
{
$id = new DDC2579Id("foo");
$assoc = new DDC2579AssocAssoc($id);
$assocAssoc = new DDC2579EntityAssoc($assoc);
$entity = new DDC2579Entity($assocAssoc);
$repository = $this->_em->getRepository(DDC2579Entity::CLASSNAME);
$this->_em->persist($assoc);
$this->_em->persist($assocAssoc);
$this->_em->persist($entity);
$this->_em->flush();
$entity->value++;
$this->_em->persist($entity);
$this->_em->flush();
$this->_em->clear();
$id = $entity->id;
$value = $entity->value;
$criteria = array('assoc' => $assoc, 'id' => $id);
$entity = $repository->findOneBy($criteria);
$this->assertInstanceOf(DDC2579Entity::CLASSNAME, $entity);
$this->assertEquals($value, $entity->value);
$this->_em->remove($entity);
$this->_em->flush();
$this->_em->clear();
$this->assertNull($repository->findOneBy($criteria));
$this->assertCount(0, $repository->findAll());
}
}
/**
* @Entity
*/
class DDC2579Entity
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="ddc2579")
*/
public $id;
/**
* @Id
* @ManyToOne(targetEntity="DDC2579EntityAssoc")
* @JoinColumn(name="relation_id", referencedColumnName="association_id")
*/
public $assoc;
/**
* @Column(type="integer")
*/
public $value;
public function __construct(DDC2579EntityAssoc $assoc, $value = 0)
{
$this->id = $assoc->assocAssoc->associationId;
$this->assoc = $assoc;
$this->value = $value;
}
}
/**
* @Entity
*/
class DDC2579EntityAssoc
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @ManyToOne(targetEntity="DDC2579AssocAssoc")
* @JoinColumn(name="association_id", referencedColumnName="associationId")
*/
public $assocAssoc;
public function __construct(DDC2579AssocAssoc $assocAssoc)
{
$this->assocAssoc = $assocAssoc;
}
}
/**
* @Entity
*/
class DDC2579AssocAssoc
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="ddc2579")
*/
public $associationId;
public function __construct(DDC2579Id $id)
{
$this->associationId = $id;
}
}
class DDC2579Type extends StringType
{
const NAME = 'ddc2579';
const CLASSNAME = __CLASS__;
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return (string)$value;
}
public function convertToPhpValue($value, AbstractPlatform $platform)
{
return new DDC2579Id($value);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return self::NAME;
}
}
class DDC2579Id
{
const CLASSNAME = __CLASS__;
private $val;
public function __construct($val)
{
$this->val = $val;
}
public function __toString()
{
return $this->val;
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @group
*/
class DDC2660Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Product'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Customer'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder')
));
} catch(\Exception $e) {
return;
}
for ($i = 0; $i < 5; $i++) {
$product = new DDC2660Product();
$customer = new DDC2660Customer();
$order = new DDC2660CustomerOrder($product, $customer, 'name' . $i);
$this->_em->persist($product);
$this->_em->persist($customer);
$this->_em->flush();
$this->_em->persist($order);
$this->_em->flush();
}
$this->_em->clear();
}
public function testIssueWithExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
public function testIssueWithoutExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
}
/**
* @Entity @Table(name="ddc_2660_product")
*/
class DDC2660Product
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer") */
class DDC2660Customer
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer_order") */
class DDC2660CustomerOrder
{
/**
* @Id @ManyToOne(targetEntity="DDC2660Product")
*/
public $product;
/**
* @Id @ManyToOne(targetEntity="DDC2660Customer")
*/
public $customer;
/**
* @Column(type="string")
*/
public $name;
public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name)
{
$this->product = $product;
$this->customer = $customer;
$this->name = $name;
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-2759
*/
class DDC2759Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'),
));
} catch(\Exception $e) {
return;
}
$qualification = new DDC2759Qualification();
$qualificationMetadata = new DDC2759QualificationMetadata($qualification);
$category1 = new DDC2759Category();
$category2 = new DDC2759Category();
$metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1);
$metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2);
$this->_em->persist($qualification);
$this->_em->persist($qualificationMetadata);
$this->_em->persist($category1);
$this->_em->persist($category2);
$this->_em->persist($metadataCategory1);
$this->_em->persist($metadataCategory2);
$this->_em->flush();
$this->_em->clear();
}
public function testCorrectNumberOfAssociationsIsReturned()
{
$repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification');
$builder = $repository->createQueryBuilder('q')
->select('q, qm, qmc')
->innerJoin('q.metadata', 'qm')
->innerJoin('qm.metadataCategories', 'qmc');
$result = $builder->getQuery()
->getArrayResult();
$this->assertCount(2, $result[0]['metadata']['metadataCategories']);
}
}
/** @Entity @Table(name="ddc_2759_qualification") */
class DDC2759Qualification
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */
public $metadata;
}
/** @Entity @Table(name="ddc_2759_category") */
class DDC2759Category
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */
public $metadataCategories;
}
/** @Entity @Table(name="ddc_2759_qualification_metadata") */
class DDC2759QualificationMetadata
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */
public $content;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */
protected $metadataCategories;
public function __construct(DDC2759Qualification $content)
{
$this->content = $content;
}
}
/** @Entity @Table(name="ddc_2759_metadata_category") */
class DDC2759MetadataCategory
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */
public $metadata;
/** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */
public $category;
public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category)
{
$this->metadata = $metadata;
$this->category = $category;
}
}

View File

@@ -977,6 +977,31 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$cm->setAttributeOverride('name', array('type'=>'date'));
}
/**
* @group DDC-2608
*/
public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
$cm->setSequenceGeneratorDefinition(array());
}
/**
* @group DDC-2662
*/
public function testQuotedSequenceName()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setSequenceGeneratorDefinition(array('sequenceName' => '`foo`'));
$this->assertEquals(array('sequenceName' => 'foo', 'quoted' => true), $cm->sequenceGeneratorDefinition);
}
}
class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy

View File

@@ -6,16 +6,21 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Tests\Mocks\ConnectionMock;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
require_once __DIR__ . '/../TestInit.php';
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
use Doctrine\Tests\OrmTestCase;
/**
* Tests the lazy-loading capabilities of the PersistentCollection.
* Tests the lazy-loading capabilities of the PersistentCollection and the initialization of collections.
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @author Austin Morris <austin.morris@gmail.com>
*/
class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
class PersistentCollectionTest extends OrmTestCase
{
/**
* @var PersistentCollection
*/
protected $collection;
private $_connectionMock;
private $_emMock;
@@ -27,6 +32,17 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
$this->_emMock = EntityManagerMock::create($this->_connectionMock);
}
/**
* Set up the PersistentCollection used for collection initialization tests.
*/
public function setUpPersistentCollection()
{
$classMetaData = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart');
$this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection);
$this->collection->setInitialized(false);
$this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products'));
}
public function testCanBePutInLazyLoadingMode()
{
$class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
@@ -34,4 +50,34 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
$collection->setInitialized(false);
$this->assertFalse($collection->isInitialized());
}
/**
* Test that PersistentCollection::current() initializes the collection.
*/
public function testCurrentInitializesCollection()
{
$this->setUpPersistentCollection();
$this->collection->current();
$this->assertTrue($this->collection->isInitialized());
}
/**
* Test that PersistentCollection::key() initializes the collection.
*/
public function testKeyInitializesCollection()
{
$this->setUpPersistentCollection();
$this->collection->key();
$this->assertTrue($this->collection->isInitialized());
}
/**
* Test that PersistentCollection::next() initializes the collection.
*/
public function testNextInitializesCollection()
{
$this->setUpPersistentCollection();
$this->collection->next();
$this->assertTrue($this->collection->isInitialized());
}
}

View File

@@ -96,6 +96,18 @@ class BasicEntityPersisterTypeValueSqlTest extends \Doctrine\Tests\OrmTestCase
public function testSelectConditionStatementIsNull()
{
$statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::IS);
$this->assertEquals('test IS ?', $statement);
$this->assertEquals('test IS NULL', $statement);
}
public function testSelectConditionStatementEqNull()
{
$statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::EQ);
$this->assertEquals('test IS NULL', $statement);
}
public function testSelectConditionStatementNeqNull()
{
$statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::NEQ);
$this->assertEquals('test IS NOT NULL', $statement);
}
}

View File

@@ -105,6 +105,8 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('bar', $q->getHint('foo'));
$this->assertEquals('baz', $q->getHint('bar'));
$this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $q->getHints());
$this->assertTrue($q->hasHint('foo'));
$this->assertFalse($q->hasHint('barFooBaz'));
}
/**
@@ -163,4 +165,16 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('cities', $parameter->getName());
$this->assertEquals($cities, $parameter->getValue());
}
/**
* @group DDC-2224
*/
public function testProcessParameterValueClassMetadata()
{
$query = $this->_em->createQuery("SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)");
$this->assertEquals(
'Doctrine\Tests\Models\CMS\CmsAddress',
$query->processParameterValue($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'))
);
}
}

View File

@@ -474,7 +474,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1",
"SELECT c0_.id AS id0, c0_.name AS name1, c0_.discr AS discr2 FROM company_persons c0_ WHERE c0_.discr IN ('employee')",
"SELECT c0_.id AS id0, c0_.name AS name1, c0_.discr AS discr2 FROM company_persons c0_ WHERE c0_.discr IN (?)",
array(), array(1 => $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'))
);
}
@@ -1710,6 +1710,18 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
'SELECT q0_."group-id" AS groupid0, q0_."group-name" AS groupname1, q1_."group-id" AS groupid2, q1_."group-name" AS groupname3 FROM "quote-group" q0_ INNER JOIN "quote-group" q1_ ON q0_."parent-id" = q1_."group-id"'
);
}
/**
* @group DDC-2506
*/
public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable()
{
$this->assertSqlGeneration(
'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1',
'SELECT c0_.id AS id0 FROM company_organizations c1_ INNER JOIN company_events c0_ ON c1_.id = c0_.org_id AND (c0_.id = ?) LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
}
class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode

View File

@@ -137,6 +137,19 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id'
);
}
public function testComplexInnerJoinWithIndexBy()
{
$qb = $this->_em->createQueryBuilder()
->select('u', 'a')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id', 'a.name');
$this->assertValidQueryBuilder(
$qb,
'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INDEX BY a.name ON u.id = a.author_id'
);
}
public function testLeftJoin()
{

View File

@@ -64,6 +64,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$metadata->mapManyToMany(array(
'fieldName' => 'comments',
'targetEntity' => 'Doctrine\Tests\ORM\Tools\EntityGeneratorComment',
'fetch' => ClassMetadataInfo::FETCH_EXTRA_LAZY,
'joinTable' => array(
'name' => 'book_comment',
'joinColumns' => array(array('name' => 'book_id', 'referencedColumnName' => 'id')),
@@ -223,6 +224,8 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals($cm->identifier, $metadata->identifier);
$this->assertEquals($cm->idGenerator, $metadata->idGenerator);
$this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName);
$this->assertEquals(ClassMetadataInfo::FETCH_EXTRA_LAZY, $cm->associationMappings['comments']['fetch']);
}
public function testLoadPrefixedMetadata()
@@ -513,9 +516,9 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
)),
array(array(
'fieldName' => 'decimal',
'phpType' => 'float',
'phpType' => 'string',
'dbType' => 'decimal',
'value' => 33.33
'value' => '12.34'
),
));
}

View File

@@ -227,6 +227,15 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
// This commit should not raise an E_NOTICE
$this->_unitOfWork->commit();
}
/**
* @group DDC-1984
*/
public function testLockWithoutEntityThrowsException()
{
$this->setExpectedException('InvalidArgumentException');
$this->_unitOfWork->lock(null, null, null);
}
}
/**

View File

@@ -124,6 +124,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\CustomType\CustomTypeParent',
'Doctrine\Tests\Models\CustomType\CustomTypeUpperCase',
),
'taxi' => array(
'Doctrine\Tests\Models\Taxi\PaidRide',
'Doctrine\Tests\Models\Taxi\Ride',
'Doctrine\Tests\Models\Taxi\Car',
'Doctrine\Tests\Models\Taxi\Driver',
),
);
protected function useModelSet($setName)
@@ -239,6 +245,14 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM customtype_uppercases');
}
if (isset($this->_usedModelSets['taxi'])) {
$conn->executeUpdate('DELETE FROM taxi_paid_ride');
$conn->executeUpdate('DELETE FROM taxi_ride');
$conn->executeUpdate('DELETE FROM taxi_car');
$conn->executeUpdate('DELETE FROM taxi_driver');
}
$this->_em->clear();
}