Compare commits

..

39 Commits
2.2.2 ... 2.2.3

Author SHA1 Message Date
Benjamin Eberlei
a65377a570 Release 2.2.3 2012-07-29 13:22:34 +02:00
Benjamin Eberlei
09d900f89f Merge branch 'DDC-1937' into 2.2 2012-07-29 11:57:37 +02:00
Benjamin Eberlei
6dfd812d3b [DDC-1937] Fix bug with apc and annotation caching using a workaround. 2012-07-29 11:57:10 +02:00
Benjamin Eberlei
14d6f364bb Fix check for ParserResult to implicit instanceof to avoid cache mixups 2012-07-29 11:40:41 +02:00
Benjamin Eberlei
0add6635c5 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-07-29 09:32:20 +02:00
Benjamin Eberlei
03622158f9 Merge branch 'DDC-1939' into 2.2 2012-07-29 09:29:56 +02:00
Benjamin Eberlei
e6512209c4 [DDC-1939] Add test for persistent collection delete with composite key 2012-07-29 09:29:17 +02:00
Marco Pivetta
63e7a0a83d DDC-1939 - Removing references to non-existing AssociationMapping class 2012-07-29 09:29:17 +02:00
Benjamin Eberlei
fe6018ce9b Merge pull request #408 from benlumley/2.2
Fix typo in remove method template in entity generator
2012-07-29 00:08:19 -07:00
Ben Lumley
c84607c3aa Fix typo in remove method template in entity generator 2012-07-25 18:51:49 +01:00
Benjamin Eberlei
5d2a3bcb3b [DDC-1775] Fix NotifyPropertyChanged Listener being attached in addIdentityMap(), which is too late for certain use-cases in the persist lifecycle. 2012-07-07 17:48:00 +02:00
Benjamin Eberlei
5739c069d3 [DDC-1846] Fix EntityRepository#find() with pessimistic locking. 2012-07-07 16:49:55 +02:00
Benjamin Eberlei
e8f4c299b5 Merge branch 'DDC-1735' into 2.2 2012-07-05 22:24:59 +02:00
Benjamin Eberlei
e123931e92 [DDC-1735] Change file_put_contents with LOCK_EX usage to temporary filename + rename, which works on Windows NFS drives. 2012-07-05 22:24:36 +02:00
Bart van den Burg
98ee3180f3 fixed DDC-1895 2012-07-05 21:59:04 +02:00
Benjamin Eberlei
e1e2248dfd [DDC-1861] Fix UnitOfWork#doMerge() 2012-07-04 21:56:11 +02:00
Benjamin Eberlei
a86bfada8b [DDC-1907] Merge from master 2012-07-04 21:09:06 +02:00
Fabio B. Silva
5f66c65c9a Fix DDC-1784 2012-05-27 19:13:46 +02:00
Benjamin Eberlei
9126f751fd Merge branch 'DDC-1798' into 2.2 2012-05-27 18:46:56 +02:00
Benjamin Eberlei
627a5a43f9 [DDC-1798] Exporter generate error when composite primary key is generated. Fixes GH-342 2012-05-27 18:45:41 +02:00
Benjamin Eberlei
f60096240d Merge branch 'DDC-1777' into 2.2 2012-05-27 17:12:42 +02:00
Benjamin Eberlei
1916349f6f [DDC-1777] Fix bug in BasicEntityPersister#exists() when no primary key is set. 2012-05-27 17:11:56 +02:00
Benjamin Eberlei
cb4e14e86d Merge branch 'DDC-1783' into 2.2 2012-05-27 12:02:38 +02:00
Benjamin Eberlei
7ada288b9b [DDC-1783] Fix memory leak in ObjectHydrator when using AbstractQuery#iterate() and EntityManager#clear() 2012-05-27 12:02:08 +02:00
Benjamin Eberlei
b7bcbc20b6 Merge pull request #356 from kdambekalns/DDC-1835
[DDC-1835] Fix clone side effects in PersistentCollection
2012-05-27 01:12:38 -07:00
Benjamin Eberlei
9dbf747bd0 Merge branch 'DDC-1799' into 2.2 2012-05-27 09:59:14 +02:00
Benjamin Eberlei
dfd734010c [DDC-1799] Fix bug in YamlExporter using OneToOne instead of ManyToOne 2012-05-27 09:58:30 +02:00
Karsten Dambekalns
f2bcb19956 [DDC-1835] Fix clone side effects in PersistentCollection 2012-05-24 18:53:34 +02:00
Philipp Scheit
1f9435de5c prevent the validator to stop with an "undefined array index"-error while validating a wrong inversedBy Attribute 2012-05-22 19:09:54 +02:00
Benjamin Eberlei
a0f4bd1134 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-05-22 19:08:49 +02:00
Philipp Scheit
ef79c1c9e8 don't call exit() in execute() 2012-05-22 19:06:13 +02:00
Benjamin Eberlei
36cac855dc Merge pull request #351 from stof/composer_requirement
Composer requirement
2012-05-20 14:18:49 -07:00
Christophe Coevoet
8abe8fe15b Fixed the composer requirement to allow the 2.2-dev version too 2012-05-20 22:48:09 +02:00
Benjamin Eberlei
202e675d2b Merge branch 'DDC-1786' into 2.2 2012-05-05 09:29:15 +02:00
Benjamin Eberlei
7a10e03507 [DDC-1786] Add note about BC in EntityManager#find(null) 2012-05-05 09:29:06 +02:00
Tim Nagel
159b6f8947 Cherry-pick GH-336 into 2.2 2012-04-16 19:00:31 +02:00
Benjamin Eberlei
6db528e713 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-04-16 18:51:02 +02:00
Tiago Ribeiro
6babe26941 Fixes autoloading of generated Annotations 2012-04-16 18:50:24 +02:00
Benjamin Eberlei
62a72603bd Bump dev version to 2.2.3 2012-04-13 10:02:34 +02:00
36 changed files with 567 additions and 85 deletions

View File

@@ -73,4 +73,9 @@ Will now return a collection of arrays with index "user" pointing to the User ob
Thousands of lines were completely reviewed and optimized for best performance.
Removed redundancy and improved code readability made now internal Doctrine code easier to understand.
Also, Doctrine 2.2 now is around 10-15% faster than 2.1.
Also, Doctrine 2.2 now is around 10-15% faster than 2.1.
# EntityManager#find(null)
Previously EntityManager#find(null) returned null. It now throws an exception.

View File

@@ -1,6 +1,6 @@
{
"name": "doctrine/orm",
"type": "library","version":"2.2.2",
"type": "library","version":"2.2.3",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
@@ -14,8 +14,8 @@
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": ">=2.2.0,<2.2.99",
"doctrine/dbal": ">=2.2.1,<2.2.99"
"doctrine/common": "2.2.*",
"doctrine/dbal": "2.2.*"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }

View File

@@ -124,8 +124,15 @@ class EntityRepository implements ObjectRepository
return null;
}
if ($lockMode !== LockMode::NONE) {
$this->_em->lock($entity, $lockMode, $lockVersion);
switch ($lockMode) {
case LockMode::OPTIMISTIC:
$this->_em->lock($entity, $lockMode, $lockVersion);
break;
case LockMode::PESSIMISTIC_READ:
case LockMode::PESSIMISTIC_WRITE:
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
$persister->refresh($sortedId, $entity, $lockMode);
break;
}
return $entity; // Hit!

View File

@@ -23,6 +23,7 @@ use PDO,
Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Events,
Doctrine\ORM\Mapping\ClassMetadata;
/**
@@ -83,6 +84,9 @@ abstract class AbstractHydrator
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$evm = $this->_em->getEventManager();
$evm->addEventListener(array(Events::onClear), $this);
$this->prepare();
return new IterableResult($this);
@@ -374,4 +378,12 @@ abstract class AbstractHydrator
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
/**
* When executed in a hydrate() loop we have to clear internal state to
* decrease memory consumption.
*/
public function onClear($eventArgs)
{
}
}

View File

@@ -54,7 +54,6 @@ class ObjectHydrator extends AbstractHydrator
private $_rootAliases = array();
private $_initializedCollections = array();
private $_existingCollections = array();
//private $_createdEntities;
/** @override */
@@ -518,4 +517,20 @@ class ObjectHydrator extends AbstractHydrator
}
}
}
/**
* When executed in a hydrate() loop we may have to clear internal state to
* decrease memory consumption.
*/
public function onClear($eventArgs)
{
parent::onClear($eventArgs);
$aliases = array_keys($this->_identifierMap);
$this->_identifierMap = array();
foreach ($aliases as $alias) {
$this->_identifierMap[$alias] = array();
}
}
}

View File

@@ -142,8 +142,12 @@ class AnnotationDriver implements Driver
$classAnnotations = $this->_reader->getClassAnnotations($class);
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
if ($classAnnotations) {
foreach ($classAnnotations as $key => $annot) {
if ( ! is_numeric($key)) {
continue;
}
$classAnnotations[get_class($annot)] = $annot;
}
}
@@ -432,8 +436,11 @@ class AnnotationDriver implements Driver
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->_reader->getMethodAnnotations($method);
if ($annotations && is_numeric(key($annotations))) {
foreach ($annotations as $annot) {
if ($annotations) {
foreach ($annotations as $key => $annot) {
if ( ! is_numeric($key)) {
continue;
}
$annotations[get_class($annot)] = $annot;
}
}

View File

@@ -284,7 +284,7 @@ final class PersistentCollection implements Collection
/**
* INTERNAL: Gets the association mapping of the collection.
*
* @return \Doctrine\ORM\Mapping\AssociationMapping
* @return array
*/
public function getMapping()
{
@@ -389,6 +389,7 @@ final class PersistentCollection implements Collection
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->owner &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
@@ -427,6 +428,7 @@ final class PersistentCollection implements Collection
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->owner &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -631,7 +633,9 @@ final class PersistentCollection implements Collection
$uow = $this->em->getUnitOfWork();
if ($this->association['type'] & ClassMetadata::TO_MANY && $this->association['orphanRemoval']) {
if ($this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval'] &&
$this->owner) {
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
// hence for event listeners we need the objects in memory.
$this->initialize();
@@ -774,13 +778,15 @@ final class PersistentCollection implements Collection
*/
public function __clone()
{
$this->initialize();
$this->owner = null;
if (is_object($this->coll)) {
$this->coll = clone $this->coll;
}
$this->initialize();
$this->owner = null;
$this->snapshot = array();
$this->changed();
}
}

View File

@@ -364,7 +364,19 @@ class BasicEntityPersister
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
$where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name'];
$params[] = $id[$idField];
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
switch (true) {
case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])):
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
break;
case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])):
$types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
break;
default:
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
}
} else {
$where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
$params[] = $id[$idField];
@@ -690,9 +702,9 @@ class BasicEntityPersister
* column or field names to values.
* @param object $entity The entity to refresh.
*/
public function refresh(array $id, $entity)
public function refresh(array $id, $entity, $lockMode = 0)
{
$sql = $this->_getSelectEntitiesSQL($id);
$sql = $this->_getSelectEntitiesSQL($id, null, $lockMode);
list($params, $types) = $this->expandParameters($id);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
@@ -1525,6 +1537,10 @@ class BasicEntityPersister
{
$criteria = $this->_class->getIdentifierValues($entity);
if ( ! $criteria) {
return false;
}
if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions);
}

View File

@@ -178,7 +178,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
}
// Composite identifier
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
$sourceClass = $this->_em->getClassMetadata(get_class($coll->getOwner()));
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];

View File

@@ -21,7 +21,6 @@ namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\AssociationMapping,
Doctrine\Common\Util\ClassUtils;
/**
@@ -184,7 +183,19 @@ class ProxyFactory
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file, LOCK_EX);
$parentDirectory = dirname($fileName);
if ( ! is_dir($parentDirectory)) {
if (false === @mkdir($parentDirectory, 0775, true)) {
throw ProxyException::proxyDirectoryNotWritable();
}
} else if ( ! is_writable($parentDirectory)) {
throw ProxyException::proxyDirectoryNotWritable();
}
$tmpFileName = $fileName . '.' . uniqid("", true);
file_put_contents($tmpFileName, $file);
rename($tmpFileName, $fileName);
}
/**

View File

@@ -21,6 +21,7 @@ namespace Doctrine\ORM;
use Doctrine\DBAL\LockMode,
Doctrine\ORM\Query\Parser,
Doctrine\ORM\Query\ParserResult,
Doctrine\ORM\Query\QueryException;
/**
@@ -218,7 +219,7 @@ final class Query extends AbstractQuery
$hash = $this->_getQueryCacheId();
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
if ($cached !== false) {
if ($cached instanceof ParserResult) {
// Cache hit.
$this->_parserResult = $cached;

View File

@@ -94,7 +94,7 @@ class QueryException extends \Doctrine\ORM\ORMException
}
/**
* @param \Doctrine\ORM\Mapping\AssociationMapping $assoc
* @param array $assoc
*/
public static function iterateWithFetchJoinCollectionNotAllowed($assoc)
{
@@ -151,4 +151,4 @@ class QueryException extends \Doctrine\ORM\ORMException
return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " .
"inheritance hierachy exists between these two classes.");
}
}
}

View File

@@ -84,6 +84,6 @@ EOT
$output->write('<info>[Database] OK - The database schema is in sync with the mapping files.</info>' . "\n");
}
exit($exit);
return $exit;
}
}

View File

@@ -20,8 +20,8 @@
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\AssociationMapping,
Doctrine\Common\Util\Inflector;
Doctrine\Common\Util\Inflector,
Doctrine\DBAL\Types\Type;
/**
* Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
@@ -136,6 +136,23 @@ public function <methodName>(<methodTypeHint>$<variableName>)
<spaces>return $this;
}';
/**
* @var string
*/
private static $_removeMethodTemplate =
'/**
* <description>
*
* @param <variableType>$<variableName>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>->removeElement($<variableName>);
}';
/**
* @var string
*/
private static $_lifecycleCallbackMethodTemplate =
'/**
* @<name>
@@ -672,6 +689,9 @@ public function <methodName>()
if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$methods[] = $code;
}
if ($code = $this->_generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$methods[] = $code;
}
if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
$methods[] = $code;
}
@@ -756,12 +776,9 @@ public function <methodName>()
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
{
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
$addMethod = end($addMethod);
$methodName = $type . $addMethod;
} else {
$methodName = $type . Inflector::classify($fieldName);
$methodName = $type . Inflector::classify($fieldName);
if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") {
$methodName = substr($methodName, 0, -1);
}
if ($this->_hasMethod($methodName, $metadata)) {
@@ -805,7 +822,7 @@ public function <methodName>()
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$replacements = array(
'<name>' => $this->_annotationsPrefix . $name,
'<name>' => $this->_annotationsPrefix . ucfirst($name),
'<methodName>' => $methodName,
);
@@ -1035,11 +1052,11 @@ public function <methodName>()
}
if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
$sequenceGenerator[] = 'allocationSize="' . $metadata->sequenceGeneratorDefinition['allocationSize'] . '"';
$sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
}
if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
$sequenceGenerator[] = 'initialValue="' . $metadata->sequenceGeneratorDefinition['initialValue'] . '"';
$sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
}
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';

View File

@@ -23,7 +23,6 @@
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\AssociationMapping,
Doctrine\ORM\Tools\EntityGenerator;
/**
@@ -69,4 +68,4 @@ class AnnotationExporter extends AbstractExporter
{
$this->_entityGenerator = $entityGenerator;
}
}
}

View File

@@ -92,7 +92,7 @@ class PhpExporter extends AbstractExporter
$lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');';
}
if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
if ( ! $metadata->isIdentifierComposite && $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');';
}
@@ -166,4 +166,4 @@ class PhpExporter extends AbstractExporter
return $export;
}
}
}

View File

@@ -127,7 +127,7 @@ class XmlExporter extends AbstractExporter
}
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType;
}
@@ -317,4 +317,4 @@ class XmlExporter extends AbstractExporter
$result = $dom->saveXML();
return $result;
}
}
}

View File

@@ -114,8 +114,8 @@ class YamlExporter extends AbstractExporter
$fieldMappings[$name] = $fieldMapping;
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $this->_getIdGeneratorTypeString($metadata->generatorType);
if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType;
}
if ($ids) {
@@ -172,7 +172,13 @@ class YamlExporter extends AbstractExporter
);
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
$array['oneToOne'][$name] = $associationMappingArray;
if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) {
$array['oneToOne'][$name] = $associationMappingArray;
} else {
$array['manyToOne'][$name] = $associationMappingArray;
}
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$oneToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],

View File

@@ -152,16 +152,18 @@ class SchemaValidator
}
// Verify inverse side/owning side match each other
$targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']];
if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well.";
if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) {
$targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']];
if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well.";
}
}
}

View File

@@ -1118,6 +1118,10 @@ class UnitOfWork implements PropertyChangedListener
if (isset($this->entityIdentifiers[$oid])) {
$this->addToIdentityMap($entity);
}
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
}
/**
@@ -1296,10 +1300,6 @@ class UnitOfWork implements PropertyChangedListener
$this->identityMap[$className][$idHash] = $entity;
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
return true;
}
@@ -1649,7 +1649,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion
return $visited[$oid]; // Prevent infinite recursion
}
$visited[$oid] = $entity; // mark visited
@@ -2356,11 +2356,12 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
$entity = $this->newInstance($class);
$oid = spl_object_hash($entity);
$oid = spl_object_hash($entity);
$this->entityIdentifiers[$oid] = $id;
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->entityIdentifiers[$oid] = $id;
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->originalEntityData[$oid] = $data;
$this->identityMap[$class->rootEntityName][$idHash] = $entity;
if ($entity instanceof NotifyPropertyChanged) {
@@ -2790,6 +2791,10 @@ class UnitOfWork implements PropertyChangedListener
$this->originalEntityData[$oid] = $data;
$this->addToIdentityMap($entity);
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
}
/**

View File

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

View File

@@ -30,12 +30,25 @@ class NavPointOfInterest
*/
private $country;
/**
* @ManyToMany(targetEntity="NavUser", cascade={"persist"})
* @JoinTable(name="navigation_pois_visitors",
* inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
* joinColumns={
* @JoinColumn(name="poi_long", referencedColumnName="nav_long"),
* @JoinColumn(name="poi_lat", referencedColumnName="nav_lat")
* }
* )
*/
private $visitors;
public function __construct($lat, $long, $name, $country)
{
$this->lat = $lat;
$this->long = $long;
$this->name = $name;
$this->country = $country;
$this->visitors = new \Doctrine\Common\Collections\ArrayCollection;
}
public function getLong() {
@@ -53,4 +66,14 @@ class NavPointOfInterest
public function getCountry() {
return $this->country;
}
public function addVisitor(NavUser $user)
{
$this->visitors[] = $user;
}
public function getVisitors()
{
return $this->visitors;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Doctrine\Tests\Models\Navigation;
/**
* @Entity
* @Table(name="navigation_users")
*/
class NavUser
{
/**
* @Id
* @Column(type="integer")
* @generatedValue
*/
private $id;
/**
* @column(type="string")
*/
private $name;
public function __construct($name)
{
$this->name = $name;
}
}

View File

@@ -448,4 +448,23 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $manager->getId());
$this->assertEquals(1, count($manager->getFriends()));
}
/**
* @group DDC-1777
*/
public function testExistsSubclass()
{
$manager = new CompanyManager();
$manager->setName('gblanco');
$manager->setSalary(1234);
$manager->setTitle('Awesome!');
$manager->setDepartment('IT');
$this->assertFalse($this->_em->getUnitOfWork()->getEntityPersister(get_class($manager))->exists($manager));
$this->_em->persist($manager);
$this->_em->flush();
$this->assertTrue($this->_em->getUnitOfWork()->getEntityPersister(get_class($manager))->exists($manager));
}
}

View File

@@ -5,6 +5,7 @@ use Doctrine\Tests\Models\Navigation\NavCountry;
use Doctrine\Tests\Models\Navigation\NavPointOfInterest;
use Doctrine\Tests\Models\Navigation\NavTour;
use Doctrine\Tests\Models\Navigation\NavPhotos;
use Doctrine\Tests\Models\Navigation\NavUser;
require_once __DIR__ . '/../../TestInit.php';
@@ -118,4 +119,26 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->setExpectedException('Doctrine\ORM\ORMException', 'The identifier long is missing for a query of Doctrine\Tests\Models\Navigation\NavPointOfInterest');
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('key1' => 100));
}
/**
* @group DDC-1939
*/
public function testDeleteCompositePersistentCollection()
{
$this->putGermanysBrandenburderTor();
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$poi->addVisitor(new NavUser("test1"));
$poi->addVisitor(new NavUser("test2"));
$this->_em->flush();
$poi->getVisitors()->clear();
$this->_em->flush();
$this->_em->clear();
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$this->assertEquals(0, count($poi->getVisitors()));
}
}

View File

@@ -40,9 +40,16 @@ class NotifyPolicyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($user);
$this->_em->persist($group);
$this->assertEquals(1, count($user->listeners));
$this->assertEquals(1, count($group->listeners));
$this->_em->flush();
$this->_em->clear();
$this->assertEquals(1, count($user->listeners));
$this->assertEquals(1, count($group->listeners));
$userId = $user->getId();
$groupId = $group->getId();
unset($user, $group);
@@ -52,6 +59,9 @@ class NotifyPolicyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$group = $this->_em->find(__NAMESPACE__.'\NotifyGroup', $groupId);
$this->assertEquals(1, $group->getUsers()->count());
$this->assertEquals(1, count($user->listeners));
$this->assertEquals(1, count($group->listeners));
$group2 = new NotifyGroup();
$group2->setName('nerds');
$this->_em->persist($group2);
@@ -63,6 +73,9 @@ class NotifyPolicyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->_em->clear();
$this->assertEquals(1, count($user->listeners));
$this->assertEquals(1, count($group->listeners));
$group2Id = $group2->getId();
unset($group2, $user);
@@ -77,7 +90,7 @@ class NotifyPolicyTest extends \Doctrine\Tests\OrmFunctionalTestCase
}
class NotifyBaseEntity implements NotifyPropertyChanged {
private $listeners = array();
public $listeners = array();
public function addPropertyChangedListener(PropertyChangedListener $listener) {
$this->listeners[] = $listener;

View File

@@ -0,0 +1,98 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Persistence\PersistentObject;
/**
*/
class PersistentCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\PersistentCollectionHolder'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\PersistentCollectionContent'),
));
} catch (\Exception $e) {
}
PersistentObject::setObjectManager($this->_em);
}
public function testPersist()
{
$collectionHolder = new PersistentCollectionHolder();
$content = new PersistentCollectionContent('first element');
$collectionHolder->addElement($content);
$this->_em->persist($collectionHolder);
$this->_em->flush();
$this->_em->clear();
$collectionHolder = $this->_em->find(__NAMESPACE__ . '\PersistentCollectionHolder', $collectionHolder->getId());
$collectionHolder->getCollection();
$content = new PersistentCollectionContent('second element');
$collectionHolder->addElement($content);
$this->assertEquals(2, $collectionHolder->getCollection()->count());
}
}
/**
* @Entity
*/
class PersistentCollectionHolder extends PersistentObject
{
/**
* @Id @Column(type="integer") @GeneratedValue
* @var int
*/
protected $id;
/**
* @var \Doctrine\Common\Collections\Collection
* @ManyToMany(targetEntity="PersistentCollectionContent", cascade={"all"})
*/
protected $collection;
public function __construct()
{
$this->collection = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @param PersistentCollectionContent $element
*/
public function addElement(PersistentCollectionContent $element)
{
$this->collection->add($element);
}
/**
* @return \Doctrine\Common\Collections\Collection
*/
public function getCollection()
{
return clone $this->collection;
}
}
/**
* @Entity
*/
class PersistentCollectionContent extends PersistentObject
{
/**
* @Id @Column(type="integer") @GeneratedValue
* @var int
*/
protected $id;
}

View File

@@ -227,6 +227,42 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
}
public function testIterateResultClearEveryCycle()
{
$article1 = new CmsArticle;
$article1->topic = "Doctrine 2";
$article1->text = "This is an introduction to Doctrine 2.";
$article2 = new CmsArticle;
$article2->topic = "Symfony 2";
$article2->text = "This is an introduction to Symfony 2.";
$this->_em->persist($article1);
$this->_em->persist($article2);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a");
$articles = $query->iterate();
$iteratedCount = 0;
$topics = array();
foreach($articles AS $row) {
$article = $row[0];
$topics[] = $article->topic;
$this->_em->clear();
$iteratedCount++;
}
$this->assertEquals(array("Doctrine 2", "Symfony 2"), $topics);
$this->assertEquals(2, $iteratedCount);
$this->_em->flush();
}
/**
* @expectedException \Doctrine\ORM\Query\QueryException
*/

View File

@@ -0,0 +1,74 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
/**
* @group DDC-1778
*/
class DDC1778Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $user;
private $phone;
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->user = new CmsUser();
$this->user->username = "beberlei";
$this->user->name = "Benjamin";
$this->user->status = "active";
$this->phone = new CmsPhoneNumber();
$this->phone->phonenumber = '0123456789';
$this->user->addPhoneNumber($this->phone);
$this->_em->persist($this->user);
$this->_em->persist($this->phone);
$this->_em->flush();
$this->_em->clear();
$this->user = $this->_em->find('Doctrine\\Tests\\Models\\CMS\\CmsUser', $this->user->getId());
$this->phone = $this->_em->find('Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber', $this->phone->phonenumber);
}
public function testClear()
{
$clonedNumbers = clone $this->user->getPhonenumbers();
$clonedNumbers->clear();
$this->_em->flush();
$this->_em->clear();
$this->user = $this->_em->find('Doctrine\\Tests\\Models\\CMS\\CmsUser', $this->user->getId());
$this->assertCount(1, $this->user->getPhonenumbers());
}
public function testRemove()
{
$clonedNumbers = clone $this->user->getPhonenumbers();
$clonedNumbers->remove(0);
$this->_em->flush();
$this->_em->clear();
$this->user = $this->_em->find('Doctrine\\Tests\\Models\\CMS\\CmsUser', $this->user->getId());
$this->assertCount(1, $this->user->getPhonenumbers());
}
public function testRemoveElement()
{
$clonedNumbers = clone $this->user->getPhonenumbers();
$clonedNumbers->removeElement($this->phone);
$this->_em->flush();
$this->_em->clear();
$this->user = $this->_em->find('Doctrine\\Tests\\Models\\CMS\\CmsUser', $this->user->getId());
$this->assertCount(1, $this->user->getPhonenumbers());
}
}

View File

@@ -101,7 +101,8 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setAuthor'), "EntityGeneratorBook::setAuthor() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getAuthor'), "EntityGeneratorBook::getAuthor() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getComments'), "EntityGeneratorBook::getComments() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'addEntityGeneratorComment'), "EntityGeneratorBook::addEntityGeneratorComment() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'addComment'), "EntityGeneratorBook::addComment() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'removeComment'), "EntityGeneratorBook::removeComment() missing.");
$this->assertEquals('published', $book->getStatus());
@@ -113,9 +114,11 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals($author, $book->getAuthor());
$comment = new EntityGeneratorComment();
$book->addEntityGeneratorComment($comment);
$book->addComment($comment);
$this->assertInstanceOf('Doctrine\Common\Collections\ArrayCollection', $book->getComments());
$this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array($comment)), $book->getComments());
$book->removeComment($comment);
$this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array()), $book->getComments());
}
public function testEntityUpdatingWorks()
@@ -221,6 +224,39 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals($classes, array_keys($p->getValue($this->_generator)));
}
/**
* @group DDC-1784
*/
public function testGenerateEntityWithSequenceGenerator()
{
$metadata = new ClassMetadataInfo($this->_namespace . '\DDC1784Entity');
$metadata->namespace = $this->_namespace;
$metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE);
$metadata->setSequenceGeneratorDefinition(array(
'sequenceName' => 'DDC1784_ID_SEQ',
'allocationSize' => 1,
'initialValue' => 2
));
$this->_generator->writeEntityClass($metadata, $this->_tmpDir);
$filename = $this->_tmpDir . DIRECTORY_SEPARATOR
. $this->_namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php';
$this->assertFileExists($filename);
require_once $filename;
$reflection = new \ReflectionProperty($metadata->name, 'id');
$docComment = $reflection->getDocComment();
$this->assertContains('@Id', $docComment);
$this->assertContains('@Column(name="id", type="integer")', $docComment);
$this->assertContains('@GeneratedValue(strategy="SEQUENCE")', $docComment);
$this->assertContains('@SequenceGenerator(sequenceName="DDC1784_ID_SEQ", allocationSize=1, initialValue=2)', $docComment);
}
public function getParseTokensInEntityFileData()
{
return array(

View File

@@ -68,10 +68,10 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
protected function _createMetadataDriver($type, $path)
{
$mappingDriver = array(
'php' => 'PHPDriver',
'php' => 'PHPDriver',
'annotation' => 'AnnotationDriver',
'xml' => 'XmlDriver',
'yaml' => 'YamlDriver',
'xml' => 'XmlDriver',
'yaml' => 'YamlDriver',
);
$this->assertArrayHasKey($type, $mappingDriver, "There is no metadata driver for the type '" . $type . "'.");
$driverName = $mappingDriver[$type];
@@ -190,7 +190,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
* @depends testIdentifierIsExported
* @param ClassMetadataInfo $class
*/
public function testFieldsAreExpored($class)
public function testFieldsAreExported($class)
{
$this->assertTrue(isset($class->fieldMappings['id']['id']) && $class->fieldMappings['id']['id'] === true);
$this->assertEquals('id', $class->fieldMappings['id']['fieldName']);
@@ -211,13 +211,12 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
}
/**
* @depends testFieldsAreExpored
* @depends testFieldsAreExported
* @param ClassMetadataInfo $class
*/
public function testOneToOneAssociationsAreExported($class)
{
$this->assertTrue(isset($class->associationMappings['address']));
//$this->assertInstanceOf('Doctrine\ORM\Mapping\OneToOneMapping', $class->associationMappings['address']);
$this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Address', $class->associationMappings['address']['targetEntity']);
$this->assertEquals('address_id', $class->associationMappings['address']['joinColumns'][0]['name']);
$this->assertEquals('id', $class->associationMappings['address']['joinColumns'][0]['referencedColumnName']);
@@ -233,6 +232,15 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
return $class;
}
/**
* @depends testFieldsAreExported
*/
public function testManyToOneAssociationsAreExported($class)
{
$this->assertTrue(isset($class->associationMappings['mainGroup']));
$this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Group', $class->associationMappings['mainGroup']['targetEntity']);
}
/**
* @depends testOneToOneAssociationsAreExported
* @param ClassMetadataInfo $class

View File

@@ -28,6 +28,11 @@ class User
*/
public $address;
/**
* @ManyToOne(targetEntity="Doctrine\Tests\ORM\Tools\Export\Group")
*/
public $mainGroup;
/**
*
* @OneToMany(targetEntity="Doctrine\Tests\ORM\Tools\Export\Phonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true)
@@ -65,4 +70,4 @@ class User
public function doStuffOnPostPersist()
{
}
}
}

View File

@@ -31,6 +31,10 @@ $metadata->mapField(array(
'columnDefinition' => 'CHAR(32) NOT NULL',
));
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
$metadata->mapManyToOne(array(
'fieldName' => 'mainGroup',
'targetEntity' => 'Doctrine\\Tests\\ORM\Tools\\Export\\Group',
));
$metadata->mapOneToOne(array(
'fieldName' => 'address',
'targetEntity' => 'Doctrine\\Tests\\ORM\\Tools\\Export\\Address',
@@ -102,4 +106,4 @@ $metadata->mapManyToMany(array(
),
),
'orderBy' => NULL,
));
));

View File

@@ -4,9 +4,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\ORM\Tools\Export\User" table="cms_users">
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
<lifecycle-callback type="prePersist" method="doOtherStuffOnPrePersistToo"/>
@@ -16,15 +16,17 @@
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
</id>
<field name="name" column="name" type="string" length="50" nullable="true" unique="true" />
<field name="email" column="user_email" type="string" column-definition="CHAR(32) NOT NULL" />
<one-to-one field="address" target-entity="Doctrine\Tests\ORM\Tools\Export\Address" inversed-by="user" orphan-removal="true">
<cascade><cascade-persist /></cascade>
<join-column name="address_id" referenced-column-name="id" on-delete="CASCADE" on-update="CASCADE"/>
</one-to-one>
<many-to-one field="mainGroup" target-entity="Doctrine\Tests\ORM\Tools\Export\Group" />
<one-to-many field="phonenumbers" target-entity="Doctrine\Tests\ORM\Tools\Export\Phonenumber" mapped-by="user" orphan-removal="true">
<cascade>
<cascade-persist/>
@@ -34,7 +36,7 @@
<order-by-field name="number" direction="ASC" />
</order-by>
</one-to-many>
<one-to-many field="interests" target-entity="Doctrine\Tests\ORM\Tools\Export\Interests" mapped-by="user" orphan-removal="true">
<cascade>
<cascade-refresh/>
@@ -44,7 +46,7 @@
<cascade-remove/>
</cascade>
</one-to-many>
<many-to-many field="groups" target-entity="Doctrine\Tests\ORM\Tools\Export\Group">
<cascade>
<cascade-all/>
@@ -58,7 +60,7 @@
</inverse-join-columns>
</join-table>
</many-to-many>
</entity>
</doctrine-mapping>

View File

@@ -26,6 +26,9 @@ Doctrine\Tests\ORM\Tools\Export\User:
cascade: [ persist ]
inversedBy: user
orphanRemoval: true
manyToOne:
mainGroup:
targetEntity: Doctrine\Tests\ORM\Tools\Export\Group
oneToMany:
phonenumbers:
targetEntity: Doctrine\Tests\ORM\Tools\Export\Phonenumber
@@ -57,4 +60,4 @@ Doctrine\Tests\ORM\Tools\Export\User:
- all
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
postPersist: [ doStuffOnPostPersist ]
postPersist: [ doStuffOnPostPersist ]

View File

@@ -88,6 +88,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\Routing\RoutingRouteBooking',
),
'navigation' => array(
'Doctrine\Tests\Models\Navigation\NavUser',
'Doctrine\Tests\Models\Navigation\NavCountry',
'Doctrine\Tests\Models\Navigation\NavPhotos',
'Doctrine\Tests\Models\Navigation\NavTour',