Compare commits

...

49 Commits
2.3.0 ... 2.1.1

Author SHA1 Message Date
Benjamin Eberlei
05d12e20be Release 2.1.1 2011-08-26 06:07:58 +00:00
Benjamin Eberlei
9b3b6052da Fix tests after updating to latest DBAL 2.1.1 release 2011-08-26 08:03:25 +02:00
Benjamin Eberlei
ffdc5e8d32 Merge remote-tracking branch 'origin/2.1.x' into 2.1.x 2011-08-24 20:45:45 +02:00
Benjamin Eberlei
6380795827 Merge branch 'DDC-1333' into 2.1.x 2011-08-21 15:06:57 +02:00
Benjamin Eberlei
6e9575b121 DDC-1333 - Fix bug in xsd 2011-08-21 15:06:50 +02:00
Benjamin Eberlei
099e2c1bd3 Merge branch 'DDC-1340' into 2.1.x 2011-08-21 15:02:47 +02:00
Benjamin Eberlei
90c47a0510 DDC-1340 - Fix bug with merge() and optimistic lock exception 2011-08-21 15:02:39 +02:00
Alexander
67133f8886 [DDC-1301] Fixed tests teardown for mysql suite 2011-08-14 19:28:15 +02:00
Benjamin Eberlei
4b4efe5483 Merge branch 'DDC-1300' into 2.1.x 2011-08-06 20:25:35 +02:00
Benjamin Eberlei
6291139dd1 DDC-1300 - Fix bug in fetch join hydration of entities with foreign key identifier 2011-08-06 20:23:47 +02:00
Benjamin Eberlei
90f96c4fba Add phar packaging target and distribute phar into download folder 2011-08-01 23:09:17 +02:00
Benjamin Eberlei
23de4a0fe4 DDC-1313 - Optimize behavior of DriverChain::getAllClassNames() 2011-08-01 21:46:41 +02:00
Benjamin Eberlei
68faa589f5 Merge branch 'DDC-1302' into 2.1.x 2011-07-31 11:35:25 +02:00
Benjamin Eberlei
7633f7b7ae DDC-1302 - Fix bug in XmlDriver not handling orphan removal 2011-07-31 11:34:56 +02:00
Benjamin Eberlei
5f665a9f3c Merge branch 'DDC-1301' into 2.1.x 2011-07-28 23:27:04 +02:00
Alexander
4819594a71 [DDC-1301] Prefixed all Legacy models properties with _ 2011-07-28 23:26:43 +02:00
Alexander
64d3715e79 [DDC-1301] Fixed count() for fetch="EXTRA_LAZY" on OneToMany association 2011-07-28 23:26:43 +02:00
Alexander
b22f692406 [DDC-1301] Added tests for fetch="EXTRA_LAZY" count() on a "legacy" database 2011-07-28 23:26:43 +02:00
Benjamin Eberlei
a1ed28a39b Merge branch 'DDC-1275' into 2.1.x 2011-07-28 23:24:38 +02:00
Michael Ridgway
9b41b6106c F[DDC-1275] ixed check for owning side of a toOne relationship 2011-07-28 23:23:28 +02:00
Michael Ridgway
a24557bc27 Removing debug comment 2011-07-28 23:23:28 +02:00
Michael Ridgway
295281c890 DDC-1275: Added join columns to result set mapping 2011-07-28 23:23:28 +02:00
Benjamin Eberlei
57c5491494 Merge branch 'DDC-1298' into 2.1.x 2011-07-27 23:29:06 +02:00
Benjamin Eberlei
b43fffce8b DDC-1298 - Fix bug in SQLWalker with derived entities that have no fields of their own. 2011-07-27 23:28:40 +02:00
Benjamin Eberlei
e426c26ade Merge branch 'DDC-1238' into 2.1.x 2011-07-27 20:52:39 +02:00
Benjamin Eberlei
e23f5bc825 DDC-1238 - Fixed a bug introduced when refactoring persisters hydration. This occurs when you call $em->clear() and you start accessing a proxy. 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
ce4f98aaab Update tests 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
16d18c7f83 DDC-1238 - Reproducible case, its correct through 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
6d62484065 Started trying to reproduce this issue 2011-07-27 20:52:15 +02:00
Benjamin Eberlei
6d1067b90c Merge branch 'DDC-1215' into 2.1.x 2011-07-26 23:01:07 +02:00
Benjamin Eberlei
3c3e5cbf41 [DDC-1215] Fix EntityGenerator inheritance regenerating properties and methods 2011-07-26 23:00:53 +02:00
Benjamin Eberlei
aabf39940a Merge branch 'DDC-1280' into 2.1.x 2011-07-26 22:31:16 +02:00
Benjamin Eberlei
1fedd0e7d3 [DDC-1280] Only generate linefeeds in proxies for consistency. 2011-07-26 22:31:06 +02:00
Benjamin Eberlei
a0a03947a3 Merge branch 'DDC-1276' into 2.1.x 2011-07-26 22:16:31 +02:00
Benjamin Eberlei
420da54620 DDC-1276 - Fix bug where merge managed and new entitiy share the same collection that is cascaded, cleared during the process and then empty afterwards. 2011-07-26 22:16:16 +02:00
Benjamin Eberlei
7bb77fc437 Merge branch 'EntityGenerator' into 2.1.x 2011-07-12 23:47:01 +02:00
Benjamin Eberlei
bb3f6957d4 DDC-1244 - Fix bug with entities without namespace 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
5ef63c1c0d DDC-1254 - Dont throw exception about missing id in disconnected metadata factory 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
09c957fdc0 DDC-1268 - Singular add*() method name through using targetEntity shortname 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
64bc782bdb Merge branch 'DDC-1240' into 2.1.x 2011-07-12 22:51:48 +02:00
Benjamin Eberlei
8fb5b40fc1 DDC-1240 - Fix optimistic lock exception loosing the message 2011-07-12 22:51:40 +02:00
Benjamin Eberlei
448811eb6a Merge branch 'DDC-1250' into 2.1.x 2011-07-09 22:14:02 +02:00
Benjamin Eberlei
ecc79c8ca0 DDC-1250 - Fix bug with inverse one to one loading and ambigious column names in certain scenarios 2011-07-09 22:13:54 +02:00
Benjamin Eberlei
d2320128cf Merge branch 'DDC-1257' into 2.1.x 2011-07-09 15:15:05 +02:00
Benjamin Eberlei
e5df347ea3 DDC-1257 - Fix bug where validation callbacks are added multiple times in EntityGenerator 2011-07-09 15:14:55 +02:00
Benjamin Eberlei
9e2b98ca16 Merge branch 'DDC-1251' into 2.1.x 2011-07-09 14:54:27 +02:00
Benjamin Eberlei
515b44126b DDC-1251 - Fix bug in token parsing of EntityGenerator 2011-07-09 14:54:10 +02:00
Benjamin Eberlei
1feac7ae9e DDC-1022 - Call __wakeup() with the same semantics then ClassMetadata::newInstance() does inside UnitOfWork 2011-07-09 12:24:47 +02:00
Benjamin Eberlei
1929ab6a75 Set DEV Version 2011-07-04 21:37:08 +00:00
47 changed files with 1144 additions and 106 deletions

View File

@@ -107,6 +107,23 @@
<target name="build" depends="test, build-orm"/>
<target name="package-phar" depends="build-orm">
<pharpackage basedir="${build.dir}/doctrine-orm/" destfile="${dist.dir}/doctrine-orm-${version}.phar" clistub="${build.dir}/doctrine-orm/bin/doctrine.php" signature="sha512">
<fileset dir="${build.dir}/doctrine-orm">
<include name="**/**" />
</fileset>
<metadata>
<element name="version" value="${version}" />
<element name="authors">
<element name="Guilherme Blanco"><element name="e-mail" value="guilhermeblanco@gmail.com" /></element>
<element name="Benjamin Eberlei"><element name="e-mail" value="kontakt@beberlei.de" /></element>
<element name="Jonathan H. Wage"><element name="e-mail" value="jonwage@gmail.com" /></element>
<element name="Roman Borschel"><element name="e-mail" value="roman@code-factory.org" /></element>
</element>
</metadata>
</pharpackage>
</target>
<!--
Runs the full test suite.
-->
@@ -206,6 +223,7 @@
<target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
<copy file="${dist.dir}/doctrine-orm-${version}.phar" todir="${project.download_dir}" />
</target>
<target name="update-dev-version">
@@ -217,7 +235,7 @@
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
<target name="release" depends="git-tag,build-packages,package-phar,distribute-download,pirum-release,update-dev-version" />
<!--
Builds distributable PEAR packages for the Symfony Dependencies

View File

@@ -92,7 +92,7 @@
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />

View File

@@ -287,4 +287,25 @@ abstract class AbstractHydrator
return $rowData;
}
protected function registerManaged($class, $entity, $data)
{
if ($class->isIdentifierComposite) {
$id = array();
foreach ($class->identifier as $fieldName) {
if (isset($class->associationMappings[$fieldName])) {
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
} else {
$id[$fieldName] = $data[$fieldName];
}
}
} else {
if (isset($class->associationMappings[$class->identifier[0]])) {
$id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
} else {
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
}
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
}

View File

@@ -205,18 +205,32 @@ class ObjectHydrator extends AbstractHydrator
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
$class = $this->_ce[$className];
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
{
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_ce[$className];
/* @var $class ClassMetadata */
if ($class->isIdentifierComposite) {
$idHash = '';
foreach ($class->identifier as $fieldName) {
$idHash .= $data[$fieldName] . ' ';
if (isset($class->associationMappings[$fieldName])) {
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
} else {
$idHash .= $data[$fieldName] . ' ';
}
}
return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
} else if (isset($class->associationMappings[$class->identifier[0]])) {
return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
} else {
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
}

View File

@@ -23,11 +23,10 @@ namespace Doctrine\ORM\Internal\Hydration;
use \PDO;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query;
class SimpleObjectHydrator extends AbstractHydrator
{
const REFRESH_ENTITY = 'doctrine_refresh_entity';
/**
* @var ClassMetadata
*/
@@ -123,17 +122,8 @@ class SimpleObjectHydrator extends AbstractHydrator
}
}
if (isset($this->_hints[self::REFRESH_ENTITY])) {
$this->_hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[$fieldName] = $data[$fieldName];
}
} else {
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
}
$this->_em->getUnitOfWork()->registerManaged($this->_hints[self::REFRESH_ENTITY], $id, $data);
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);

View File

@@ -313,28 +313,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
$this->validateRuntimeMetadata($class, $parent);
$this->loadedMetadata[$className] = $class;
@@ -351,6 +330,38 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
return $loaded;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
}
/**
* Creates a new ClassMetadata instance for the given class name.
*

View File

@@ -88,15 +88,20 @@ class DriverChain implements Driver
public function getAllClassNames()
{
$classNames = array();
$driverClasses = array();
foreach ($this->_drivers AS $namespace => $driver) {
$driverClasses = $driver->getAllClassNames();
foreach ($driverClasses AS $className) {
$oid = spl_object_hash($driver);
if (!isset($driverClasses[$oid])) {
$driverClasses[$oid] = $driver->getAllClassNames();
}
foreach ($driverClasses[$oid] AS $className) {
if (strpos($className, $namespace) === 0) {
$classNames[] = $className;
$classNames[$className] = true;
}
}
}
return array_unique($classNames);
return array_keys($classNames);
}
/**

View File

@@ -285,8 +285,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
}
if (isset($oneToOneElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement->{'orphan-removal'};
if (isset($oneToOneElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphan-removal'];
}
$metadata->mapOneToOne($mapping);
@@ -310,8 +310,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
}
if (isset($oneToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
if (isset($oneToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphan-removal'];
}
if (isset($oneToManyElement->{'order-by'})) {

View File

@@ -33,6 +33,7 @@ class OptimisticLockException extends ORMException
public function __construct($msg, $entity)
{
parent::__construct($msg);
$this->entity = $entity;
}

View File

@@ -570,6 +570,7 @@ class BasicEntityPersister
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
}
if ($this->_selectJoinSql) {
@@ -587,13 +588,12 @@ class BasicEntityPersister
*
* @param array $assoc The association to load.
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
* @param object $targetEntity The existing ghost entity (proxy) to load, if any.
* @param array $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
*/
public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array())
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
{
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
return $foundEntity;
@@ -621,7 +621,7 @@ class BasicEntityPersister
}
*/
$targetEntity = $this->load($identifier, $targetEntity, $assoc, $hints);
$targetEntity = $this->load($identifier, null, $assoc, $hints);
// Complete bidirectional association, if necessary
if ($targetEntity !== null && $isInverseSingleValued) {
@@ -633,7 +633,11 @@ class BasicEntityPersister
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
// unset the old value and set the new sql aliased value here. By definition
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
unset($identifier[$targetKeyColumn]);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
@@ -641,7 +645,7 @@ class BasicEntityPersister
}
}
$targetEntity = $this->load($identifier, $targetEntity, $assoc);
$targetEntity = $this->load($identifier, null, $assoc);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
@@ -1214,7 +1218,7 @@ class BasicEntityPersister
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
// very careless developers could potentially open up this normally hidden api for userland attacks,
// therefore checking for spaces and function calls which are not allowed.
// found a join column condition, not really a "field"
$conditionSql .= $field;
} else {

View File

@@ -124,24 +124,26 @@ class OneToManyPersister extends AbstractCollectionPersister
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$params = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
$where = '';
foreach ($class->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
if ($where != '') {
$where .= ' AND ';
}
$where .= $joinColumn['name'] . " = ?";
if ($class->containsForeignIdentifier) {
$params[] = $id[$class->getFieldForColumn($joinColumn['referencedColumnName'])];
if ($targetClass->containsForeignIdentifier) {
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
} else {
$params[] = $id[$class->fieldNames[$joinColumn['referencedColumnName']]];
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
}
$sql = "SELECT count(*) FROM " . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
return $this->_conn->fetchColumn($sql, $params);
}
@@ -180,4 +182,4 @@ class OneToManyPersister extends AbstractCollectionPersister
return $uow->getEntityPersister($mapping['targetEntity'])
->exists($element, array($mapping['mappedBy'] => $id));
}
}
}

View File

@@ -172,7 +172,7 @@ class ProxyFactory
}
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= PHP_EOL . ' public function ';
$methods .= "\n" . ' public function ';
if ($method->returnsReference()) {
$methods .= '&';
}
@@ -208,10 +208,10 @@ class ProxyFactory
}
$methods .= $parameterString . ')';
$methods .= PHP_EOL . ' {' . PHP_EOL;
$methods .= ' $this->__load();' . PHP_EOL;
$methods .= "\n" . ' {' . "\n";
$methods .= ' $this->__load();' . "\n";
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= PHP_EOL . ' }' . PHP_EOL;
$methods .= "\n" . ' }' . "\n";
}
}
@@ -274,6 +274,14 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
if (method_exists($this, "__wakeup")) {
// call this after __isInitialized__to avoid infinite recursion
// but before loading to emulate what ClassMetadata::newInstance()
// provides.
$this->__wakeup();
}
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}

View File

@@ -53,6 +53,15 @@ final class Query extends AbstractQuery
* @var string
*/
const HINT_REFRESH = 'doctrine.refresh';
/**
* Internal hint: is set to the proxy entity that is currently triggered for loading
*
* @var string
*/
const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
/**
* The forcePartialLoad query hint forces a particular query to return
* partial objects.

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
@@ -52,21 +53,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
{
$this->addEntityResult($class, $alias);
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
}
$platform = $this->em->getConnection()->getDatabasePlatform();
foreach ($classMetadata->getColumnNames() AS $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
@@ -81,6 +68,14 @@ class ResultSetMappingBuilder extends ResultSetMapping
public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array())
{
$this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
* Adds all fields of the given class to the result set mapping (columns and meta fields)
*/
protected function addAllClassFields($class, $alias, $renamedColumns = array())
{
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
@@ -96,5 +91,17 @@ class ResultSetMappingBuilder extends ResultSetMapping
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
foreach ($classMetadata->associationMappings AS $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] AS $joinColumn) {
$columnName = $joinColumn['name'];
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName));
}
}
}
}
}

View File

@@ -527,7 +527,7 @@ class SqlWalker implements TreeWalker
public function walkSelectClause($selectClause)
{
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode(
', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)
', ', array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions))
);
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&

View File

@@ -52,6 +52,17 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
return $metadata;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
// validate nothing
}
/**
* @override
*/

View File

@@ -438,7 +438,7 @@ public function <methodName>()
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . '\\' . $token[1];
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
$this->_staticReflection[$lastSeenClass]['properties'] = array();
$this->_staticReflection[$lastSeenClass]['methods'] = array();
}
@@ -451,7 +451,7 @@ public function <methodName>()
} else if ($token[0] == T_FUNCTION) {
if ($tokens[$i+2][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
} else if ($tokens[$i+2][0] == T_AMPERSAND && $tokens[$i+3][0] == T_STRING) {
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
}
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
@@ -462,6 +462,14 @@ public function <methodName>()
private function _hasProperty($property, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate property if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasProperty($property)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($property, $this->_staticReflection[$metadata->name]['properties'])
@@ -470,6 +478,14 @@ public function <methodName>()
private function _hasMethod($method, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate method if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasMethod($method)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($method, $this->_staticReflection[$metadata->name]['methods'])
@@ -686,11 +702,18 @@ public function <methodName>()
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
{
$methodName = $type . Inflector::classify($fieldName);
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
$addMethod = end($addMethod);
$methodName = $type . $addMethod;
} else {
$methodName = $type . Inflector::classify($fieldName);
}
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$var = sprintf('_%sMethodTemplate', $type);
$template = self::$$var;
@@ -723,6 +746,7 @@ public function <methodName>()
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$replacements = array(
'<name>' => $this->_annotationsPrefix . $name,

View File

@@ -1406,7 +1406,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
}
}
@@ -1456,7 +1456,8 @@ class UnitOfWork implements PropertyChangedListener
}
if ($assoc2['isCascadeMerge']) {
$managedCol->initialize();
if (!$managedCol->isEmpty()) {
// clear and set dirty a managed collection if its not also the same collection to merge from.
if (!$managedCol->isEmpty() && $managedCol != $mergeCol) {
$managedCol->unwrap()->clear();
$managedCol->setDirty(true);
if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
@@ -1655,6 +1656,10 @@ class UnitOfWork implements PropertyChangedListener
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
continue;
}
if ($relatedEntities instanceof PersistentCollection) {
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
@@ -1976,7 +1981,7 @@ class UnitOfWork implements PropertyChangedListener
// a way to solve this with deferred eager loading, which means putting
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
$newValue = $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
->loadOneToOneEntity($assoc, $entity, $associatedId);
} else {
// Deferred eager load only works for single identifier classes
@@ -2012,7 +2017,7 @@ class UnitOfWork implements PropertyChangedListener
} else {
// Inverse side of x-to-one can never be lazy
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null));
->loadOneToOneEntity($assoc, $entity));
}
} else {
// Inject collection
@@ -2143,7 +2148,7 @@ class UnitOfWork implements PropertyChangedListener
* @return array The identifier values.
*/
public function getEntityIdentifier($entity)
{
{
return $this->entityIdentifiers[spl_object_hash($entity)];
}

View File

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

View File

@@ -27,6 +27,11 @@ class DDC117Article
*/
private $translations;
/**
* @OneToMany(targetEntity="DDC117Link", mappedBy="source")
*/
private $links;
public function __construct($title)
{
$this->title = $title;

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* Foreign Key Entity without additional fields!
*
* @Entity
*/
class DDC117Link
{
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article", inversedBy="links")
* @JoinColumn(name="source_id", referencedColumnName="article_id")
*/
public $source;
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article")
* @JoinColumn(name="target_id", referencedColumnName="article_id")
*/
public $target;
public function __construct($source, $target, $description)
{
$this->source = $source;
$this->target = $target;
}
}

View File

@@ -34,7 +34,7 @@ class ECommerceCustomer
* only one customer at the time, while a customer can choose only one
* mentor. Not properly appropriate but it works.
*
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"}, fetch="EAGER")
* @JoinColumn(name="mentor_id", referencedColumnName="id")
*/
private $mentor;

View File

@@ -56,6 +56,7 @@ class ECommerceProduct
private $related;
public $isCloned = false;
public $wakeUp = false;
public function __construct()
{
@@ -166,4 +167,12 @@ class ECommerceProduct
{
$this->isCloned = true;
}
/**
* Testing docblock contents here
*/
public function __wakeup()
{
$this->wakeUp = true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
/**
* @Entity
* @Table(name="legacy_articles")
*/
class LegacyArticle
{
/**
* @Id
* @Column(name="iArticleId", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $_id;
/**
* @Column(name="sTopic", type="string", length=255)
*/
public $_topic;
/**
* @Column(name="sText", type="text")
*/
public $_text;
/**
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_articles")
* @JoinColumn(name="iUserId", referencedColumnName="iUserId")
*/
public $_user;
public function setAuthor(LegacyUser $author) {
$this->_user = $author;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="legacy_cars")
*/
class LegacyCar
{
/**
* @Id
* @GeneratedValue
* @Column(name="iCarId", type="integer", nullable=false)
*/
public $_id;
/**
* @ManyToMany(targetEntity="LegacyUser", mappedBy="_cars")
*/
public $_users;
/**
* @Column(name="sDescription", type="string", length=255, unique=true)
*/
public $_description;
function getDescription()
{
return $this->_description;
}
public function addUser(LegacyUser $user) {
$this->_users[] = $user;
}
public function getUsers() {
return $this->_users;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="legacy_users")
*/
class LegacyUser
{
/**
* @Id
* @GeneratedValue
* @Column(name="iUserId", type="integer", nullable=false)
*/
public $_id;
/**
* @Column(name="sUsername", type="string", length=255, unique=true)
*/
public $_username;
/**
* @Column(type="string", length=255)
*/
public $_name;
/**
* @OneToMany(targetEntity="LegacyArticle", mappedBy="_user")
*/
public $_articles;
/**
* @OneToMany(targetEntity="LegacyUserReference", mappedBy="_source", cascade={"remove"})
*/
public $_references;
/**
* @ManyToMany(targetEntity="LegacyCar", inversedBy="_users", cascade={"persist", "merge"})
* @JoinTable(name="legacy_users_cars",
* joinColumns={@JoinColumn(name="iUserId", referencedColumnName="iUserId")},
* inverseJoinColumns={@JoinColumn(name="iCarId", referencedColumnName="iCarId")}
* )
*/
public $_cars;
public function __construct() {
$this->_articles = new ArrayCollection;
$this->_references = new ArrayCollection;
$this->_cars = new ArrayCollection;
}
public function getId() {
return $this->_id;
}
public function getUsername() {
return $this->_username;
}
public function addArticle(LegacyArticle $article) {
$this->_articles[] = $article;
$article->setAuthor($this);
}
public function addReference($reference)
{
$this->_references[] = $reference;
}
public function references()
{
return $this->_references;
}
public function addCar(LegacyCar $car) {
$this->_cars[] = $car;
$car->addUser($this);
}
public function getCars() {
return $this->_cars;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
/**
* @Entity
* @Table(name="legacy_users_reference")
*/
class LegacyUserReference
{
/**
* @Id
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_references")
* @JoinColumn(name="iUserIdSource", referencedColumnName="iUserId")
*/
private $_source;
/**
* @Id
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_references")
* @JoinColumn(name="iUserIdTarget", referencedColumnName="iUserId")
*/
private $_target;
/**
* @column(type="string")
*/
private $_description;
/**
* @column(type="datetime")
*/
private $_created;
public function __construct($source, $target, $description)
{
$source->addReference($this);
$target->addReference($this);
$this->_source = $source;
$this->_target = $target;
$this->_description = $description;
$this->_created = new \DateTime("now");
}
public function source()
{
return $this->_source;
}
public function target()
{
return $this->_target;
}
public function setDescription($desc)
{
$this->_description = $desc;
}
public function getDescription()
{
return $this->_description;
}
}

View File

@@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../TestInit.php';
@@ -192,5 +193,26 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($this->_em->contains($user));
$this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user));
}
/**
* @group DDC-1340
*/
public function testMergeArticleWrongVersion()
{
$article = new CmsArticle();
$article->topic = "test";
$article->text = "test";
$this->_em->persist($article);
$this->_em->flush();
$this->_em->detach($article);
$sql = "UPDATE cms_articles SET version = version+1 WHERE id = " . $article->id;
$this->_em->getConnection()->executeUpdate($sql);
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock failed, version 1 was expected, but is actually 2');
$this->_em->merge($article);
}
}

View File

@@ -52,6 +52,48 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Roman', $users[0]->name);
}
public function testBasicNativeQueryWithMetaResult()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$addr = new CmsAddress;
$addr->country = 'germany';
$addr->zip = 10827;
$addr->city = 'Berlin';
$user->setAddress($addr);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsAddress', 'a');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('id'), 'id');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('country'), 'country');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('zip'), 'zip');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('city'), 'city');
$rsm->addMetaResult('a', $this->platform->getSQLResultCasing('user_id'), 'user_id');
$query = $this->_em->createNativeQuery('SELECT a.id, a.country, a.zip, a.city, a.user_id FROM cms_addresses a WHERE a.id = ?', $rsm);
$query->setParameter(1, $addr->id);
$addresses = $query->getResult();
$this->assertEquals(1, count($addresses));
$this->assertTrue($addresses[0] instanceof CmsAddress);
$this->assertEquals($addr->country, $addresses[0]->country);
$this->assertEquals($addr->zip, $addresses[0]->zip);
$this->assertEquals($addr->city, $addresses[0]->city);
$this->assertEquals($addr->street, $addresses[0]->street);
$this->assertTrue($addresses[0]->user instanceof CmsUser);
}
public function testJoinedOneToManyNativeQuery()
{
@@ -193,6 +235,17 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$phones = $users[0]->getPhonenumbers();
$this->assertEquals(424242, $phones[0]->phonenumber);
$this->assertTrue($phones[0]->getUser() === $users[0]);
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p');
$query = $this->_em->createNativeQuery('SELECT p.* FROM cms_phonenumbers p WHERE p.phonenumber = ?', $rsm);
$query->setParameter(1, $phone->phonenumber);
$phone = $query->getSingleResult();
$this->assertNotNull($phone->getUser());
$this->assertEquals($user->name, $phone->getUser()->getName());
}
public function testJoinedOneToOneNativeQueryWithRSMBuilder()
@@ -235,6 +288,17 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('germany', $users[0]->getAddress()->getCountry());
$this->assertEquals(10827, $users[0]->getAddress()->getZipCode());
$this->assertEquals('Berlin', $users[0]->getAddress()->getCity());
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a');
$query = $this->_em->createNativeQuery('SELECT a.* FROM cms_addresses a WHERE a.id = ?', $rsm);
$query->setParameter(1, $addr->getId());
$address = $query->getSingleResult();
$this->assertNotNull($address->getUser());
$this->assertEquals($user->name, $address->getUser()->getName());
}
/**

View File

@@ -49,6 +49,14 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->assertForeignKeyIs(null);
}
public function testFind()
{
$id = $this->_createFixture();
$customer = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $id);
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $customer->getMentor());
}
public function testEagerLoadsAssociation()
{
$this->_createFixture();
@@ -127,6 +135,8 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->_em->flush();
$this->_em->clear();
return $customer->getId();
}
}

View File

@@ -130,4 +130,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertEquals('Doctrine 2 Cookbook', $entity->getName());
}
/**
* @group DDC-1022
*/
public function testWakeupCalledOnProxy()
{
$id = $this->createProduct();
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertFalse($entity->wakeUp);
$entity->setName('Doctrine 2 Cookbook');
$this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup().");
}
}

View File

@@ -30,10 +30,10 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]);
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]);
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]);
$this->assertEquals(8, count($sql));
}

View File

@@ -44,10 +44,10 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", $sql[8]);
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[9]);
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[10]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
$this->assertEquals(count($sql), 15);
}
@@ -90,9 +90,14 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
);
$tool = new SchemaTool($this->_em);
try {
$tool->createSchema($classes);
} catch(\Exception $e) {
}
$sql = $tool->getDropSchemaSQL($classes);
$this->assertEquals(13, count($sql));
$this->assertEquals(10, count($sql));
$dropSequenceSQLs = 0;
foreach ($sql AS $stmt) {
if (strpos($stmt, "DROP SEQUENCE") === 0) {

View File

@@ -0,0 +1,96 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsEmployee;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1238
*/
class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1238User'),
));
} catch(\PDOException $e) {
}
}
public function testIssue()
{
$user = new DDC1238User;
$user->setName("test");
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$userId = $user->getId();
$this->_em->clear();
$user = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->_em->clear();
$userId2 = $user->getId();
$this->assertEquals($userId, $userId2, "This proxy can still be initialized.");
}
public function testIssueProxyClear()
{
$user = new DDC1238User;
$user->setName("test");
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$userId = $user->getId();
$this->_em->clear();
$user = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->_em->clear();
$user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type");
}
}
/**
* @Entity
*/
class DDC1238User
{
/** @Id @GeneratedValue @Column(type="integer") */
private $id;
/**
* @Column
* @var string
*/
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsEmployee;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1250
*/
class DDC1250Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1250ClientHistory'),
));
} catch(\PDOException $e) {
}
}
public function testIssue()
{
$c1 = new DDC1250ClientHistory;
$c2 = new DDC1250ClientHistory;
$c1->declinedClientsHistory = $c2;
$c1->declinedBy = $c2;
$c2->declinedBy = $c1;
$c2->declinedClientsHistory= $c1;
$this->_em->persist($c1);
$this->_em->persist($c2);
$this->_em->flush();
$this->_em->clear();
$history = $this->_em->createQuery('SELECT h FROM ' . __NAMESPACE__ . '\\DDC1250ClientHistory h WHERE h.id = ?1')
->setParameter(1, $c2->id)->getSingleResult();
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC1250ClientHistory', $history);
}
}
/**
* @Entity
*/
class DDC1250ClientHistory
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/** @OneToOne(targetEntity="DDC1250ClientHistory", inversedBy="declinedBy")
* @JoinColumn(name="declined_clients_history_id", referencedColumnName="id")
*/
public $declinedClientsHistory;
/**
* @OneToOne(targetEntity="DDC1250ClientHistory", mappedBy="declinedClientsHistory")
* @var
*/
public $declinedBy;
}
/**
*
Entities\ClientsHistory:
type: entity
table: clients_history
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
[...skiped...]
oneToOne:
declinedClientsHistory:
targetEntity: Entities\ClientsHistory
joinColumn:
name: declined_clients_history_id
referencedColumnName: id
inversedBy: declinedBy
declinedBy:
targetEntity: Entities\ClientsHistory
mappedBy: declinedClientsHistory
lifecycleCallbacks: { }
repositoryClass: Entities\ClientsHistoryRepository
*/

View File

@@ -0,0 +1,50 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1276
*/
class DDC1276Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$user = new CmsUser();
$user->name = "Benjamin";
$user->username = "beberlei";
$user->status = "active";
$this->_em->persist($user);
for ($i = 0; $i < 2; $i++) {
$group = new CmsGroup();
$group->name = "group".$i;
$user->groups[] = $group;
$this->_em->persist($group);
}
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $user->id);
$cloned = clone $user;
$this->assertSame($user->groups, $cloned->groups);
$this->assertEquals(2, count($user->groups));
$this->_em->merge($cloned);
$this->assertEquals(2, count($user->groups));
$this->_em->flush();
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1300
*/
class DDC1300Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1300Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1300FooLocale'),
));
}
public function testIssue()
{
$foo = new DDC1300Foo();
$foo->_fooReference = "foo";
$this->_em->persist($foo);
$this->_em->flush();
$locale = new DDC1300FooLocale();
$locale->_foo = $foo;
$locale->_locale = "en";
$locale->_title = "blub";
$this->_em->persist($locale);
$this->_em->flush();
$query = $this->_em->createQuery('SELECT f, fl FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1300Foo f JOIN f._fooLocaleRefFoo fl');
$result = $query->getResult();
$this->assertEquals(1, count($result));
}
}
/**
* @Entity
*/
class DDC1300Foo
{
/**
* @var integer fooID
* @Column(name="fooID", type="integer", nullable=false)
* @GeneratedValue(strategy="AUTO")
* @Id
*/
public $_fooID = null;
/**
* @var string fooReference
* @Column(name="fooReference", type="string", nullable=true, length=45)
*/
public $_fooReference = null;
/**
* @OneToMany(targetEntity="DDC1300FooLocale", mappedBy="_foo",
* cascade={"persist"})
*/
public $_fooLocaleRefFoo = null;
/**
* Constructor
*
* @param array|Zend_Config|null $options
* @return Bug_Model_Foo
*/
public function __construct($options = null)
{
$this->_fooLocaleRefFoo = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/**
* @Entity
*/
class DDC1300FooLocale
{
/**
* @ManyToOne(targetEntity="DDC1300Foo")
* @JoinColumn(name="fooID", referencedColumnName="fooID")
* @Id
*/
public $_foo = null;
/**
* @var string locale
* @Column(name="locale", type="string", nullable=false, length=5)
* @Id
*/
public $_locale = null;
/**
* @var string title
* @Column(name="title", type="string", nullable=true, length=150)
*/
public $_title = null;
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @author asm89
*/
class DDC1301Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $userId;
public function setUp()
{
$this->useModelSet('legacy');
parent::setUp();
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Legacy\LegacyUser');
$class->associationMappings['_articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['_references']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['_cars']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$this->loadFixture();
}
public function tearDown()
{
parent::tearDown();
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Legacy\LegacyUser');
$class->associationMappings['_articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['_references']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['_cars']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
}
public function testCountNotInitializesLegacyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_articles->isInitialized());
$this->assertEquals(2, count($user->_articles));
$this->assertFalse($user->_articles->isInitialized());
foreach ($user->_articles AS $article) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function testCountNotInitializesLegacyCollectionWithForeignIdentifier()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_references->isInitialized());
$this->assertEquals(2, count($user->_references));
$this->assertFalse($user->_references->isInitialized());
foreach ($user->_references AS $reference) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function testCountNotInitializesLegacyManyToManyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_cars->isInitialized());
$this->assertEquals(3, count($user->_cars));
$this->assertFalse($user->_cars->isInitialized());
foreach ($user->_cars AS $reference) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user1->_username = "beberlei";
$user1->_name = "Benjamin";
$user1->_status = "active";
$user2 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user2->_username = "jwage";
$user2->_name = "Jonathan";
$user2->_status = "active";
$user3 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user3->_username = "romanb";
$user3->_name = "Roman";
$user3->_status = "active";
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($user3);
$article1 = new \Doctrine\Tests\Models\Legacy\LegacyArticle();
$article1->_topic = "Test";
$article1->_text = "Test";
$article1->setAuthor($user1);
$article2 = new \Doctrine\Tests\Models\Legacy\LegacyArticle();
$article2->_topic = "Test";
$article2->_text = "Test";
$article2->setAuthor($user1);
$this->_em->persist($article1);
$this->_em->persist($article2);
$car1 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car1->_description = "Test1";
$car2 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car2->_description = "Test2";
$car3 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car3->_description = "Test3";
$user1->addCar($car1);
$user1->addCar($car2);
$user1->addCar($car3);
$user2->addCar($car1);
$user3->addCar($car1);
$this->_em->persist($car1);
$this->_em->persist($car2);
$this->_em->persist($car3);
$this->_em->flush();
$detail1 = new \Doctrine\Tests\Models\Legacy\LegacyUserReference($user1, $user2, "foo");
$detail2 = new \Doctrine\Tests\Models\Legacy\LegacyUserReference($user1, $user3, "bar");
$this->_em->persist($detail1);
$this->_em->persist($detail2);
$this->_em->flush();
$this->_em->clear();
$this->userId = $user1->getId();
}
}

View File

@@ -186,6 +186,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']);
$this->assertTrue($class->associationMappings['phonenumbers']['orphanRemoval']);
// Test Order By
$this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']['orderBy']);
@@ -329,7 +330,7 @@ class User
public $address;
/**
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"})
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
* @OrderBy({"number"="ASC"})
*/
public $phonenumbers;
@@ -425,7 +426,7 @@ class User
1 => 'persist',
),
'mappedBy' => 'user',
'orphanRemoval' => false,
'orphanRemoval' => true,
'orderBy' =>
array(
'number' => 'ASC',

View File

@@ -64,7 +64,7 @@ $metadata->mapOneToMany(array(
1 => 'persist',
),
'mappedBy' => 'user',
'orphanRemoval' => false,
'orphanRemoval' => true,
'orderBy' =>
array(
'number' => 'ASC',

View File

@@ -39,7 +39,7 @@
<join-column name="address_id" referenced-column-name="id" on-delete="CASCADE" on-update="CASCADE"/>
</one-to-one>
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user">
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user" orphan-removal="true">
<cascade>
<cascade-persist/>
</cascade>

View File

@@ -35,6 +35,7 @@ Doctrine\Tests\ORM\Mapping\User:
oneToMany:
phonenumbers:
targetEntity: Phonenumber
orphanRemoval: true
mappedBy: user
orderBy:
number: ASC

View File

@@ -922,6 +922,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"SELECT COALESCE(NULLIF(c0_.name, ''), c0_.username) AS sclr0 FROM cms_users c0_"
);
}
/**
* @group DDC-1298
*/
public function testSelectForeignKeyPKWithoutFields()
{
$this->assertSqlGeneration(
"SELECT t, s, l FROM Doctrine\Tests\Models\DDC117\DDC117Link l INNER JOIN l.target t INNER JOIN l.source s",
"SELECT d0_.article_id AS article_id0, d0_.title AS title1, d1_.article_id AS article_id2, d1_.title AS title3 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id"
);
}
}

View File

@@ -98,7 +98,7 @@ 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', 'addComments'), "EntityGeneratorBook::addComments() missing.");
$this->assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'addEntityGeneratorComment'), "EntityGeneratorBook::addEntityGeneratorComment() missing.");
$this->assertEquals('published', $book->getStatus());
@@ -110,7 +110,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals($author, $book->getAuthor());
$comment = new EntityGeneratorComment();
$book->addComments($comment);
$book->addEntityGeneratorComment($comment);
$this->assertInstanceOf('Doctrine\Common\Collections\ArrayCollection', $book->getComments());
$this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array($comment)), $book->getComments());
}

View File

@@ -98,12 +98,19 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\DDC117\DDC117ArticleDetails',
'Doctrine\Tests\Models\DDC117\DDC117ApproveChanges',
'Doctrine\Tests\Models\DDC117\DDC117Editor',
'Doctrine\Tests\Models\DDC117\DDC117Link',
),
'stockexchange' => array(
'Doctrine\Tests\Models\StockExchange\Bond',
'Doctrine\Tests\Models\StockExchange\Stock',
'Doctrine\Tests\Models\StockExchange\Market',
),
'legacy' => array(
'Doctrine\Tests\Models\Legacy\LegacyUser',
'Doctrine\Tests\Models\Legacy\LegacyUserReference',
'Doctrine\Tests\Models\Legacy\LegacyArticle',
'Doctrine\Tests\Models\Legacy\LegacyCar',
),
);
protected function useModelSet($setName)
@@ -191,6 +198,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM ddc117editor_ddc117translation');
$conn->executeUpdate('DELETE FROM DDC117Editor');
$conn->executeUpdate('DELETE FROM DDC117ApproveChanges');
$conn->executeUpdate('DELETE FROM DDC117Link');
$conn->executeUpdate('DELETE FROM DDC117Reference');
$conn->executeUpdate('DELETE FROM DDC117ArticleDetails');
$conn->executeUpdate('DELETE FROM DDC117Translation');
@@ -202,6 +210,13 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM exchange_stocks');
$conn->executeUpdate('DELETE FROM exchange_markets');
}
if (isset($this->_usedModelSets['legacy'])) {
$conn->executeUpdate('DELETE FROM legacy_users_cars');
$conn->executeUpdate('DELETE FROM legacy_users_reference');
$conn->executeUpdate('DELETE FROM legacy_articles');
$conn->executeUpdate('DELETE FROM legacy_cars');
$conn->executeUpdate('DELETE FROM legacy_users');
}
$this->_em->clear();
}