Compare commits

...

33 Commits
2.2.0 ... 2.2.1

Author SHA1 Message Date
Benjamin Eberlei
78fa740b94 Release 2.2.1 2012-03-03 22:26:27 +01:00
Benjamin Eberlei
715fdd7559 Merge branch 'DDC-1668' into 2.2 2012-03-03 22:25:36 +01:00
Benjamin Eberlei
884ef42075 [DDC-1668] Fix problem with the is_int fowards compatibility check. Its not really necesssary anymore, we should remove this code in the future. 2012-03-03 22:25:11 +01:00
Benjamin Eberlei
5d3a626c2b Merge branch 'DDC-1678' into 2.2 2012-03-03 22:17:07 +01:00
Francisco Facioni
587dda90d3 UnitTest for ManyToMany update notification 2012-03-03 22:16:44 +01:00
Francisco Facioni
21ae0d2a45 When using a ManyToMany relationship no listener is notified about any change to the owning entity.
What I'm doing with this patch is marking the entity for update when there is a modification in the ManyToMany relationship so the listeners are notified about it.

The main reason for this is for hooking up services like Solr or other indexers to update the entities even for ManyToMany relationships.
2012-03-03 22:16:44 +01:00
Benjamin Eberlei
f4dc533127 Bump Common to 2.2.1 2012-03-03 22:08:36 +01:00
Benjamin Eberlei
c9d1b3a428 Merge branch 'DDC-1652' into 2.2 2012-03-03 21:11:15 +01:00
Benjamin Eberlei
9170e1b810 [DDC-1652] Fix SqlWalker to include foreign key identifiers in SQL SELECT statement no matter what the meta column setting is suggesting. 2012-03-03 21:10:33 +01:00
Benjamin Eberlei
f1d2a79a87 Merge branch 'DDC-1673' into 2.2 2012-03-03 19:44:27 +01:00
Guilherme Blanco
dd2f3967cd [DDC-1673] Fixed unused in ProxyFactory. 2012-03-03 19:26:27 +01:00
Benjamin Eberlei
4100a6a7a3 Merge branch 'DDC-1667' into 2.2 2012-03-03 19:11:05 +01:00
Guilherme Blanco
bfb590459e [DDC-1667] Removed implicit obligation to define an Index and UniqueConstraint name. It is optional, but Annotations Driver was broken if not defined. 2012-03-03 19:10:06 +01:00
Sergio Moya
191520439b No unique join column fields for Single Table inheritance type. 2012-02-20 15:56:11 +01:00
Benjamin Eberlei
31709b4c1a Merge branch 'DDC-1649' into 2.2 2012-02-20 15:40:07 +01:00
Benjamin Eberlei
af6de10c5b [DDC-1649] Fix notice by last commit. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei
f4a78e13ee [DDC-1649] Add additional check for not allowed mapping of dependent association keys. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei
bd37b83f4f Merge branch 'DDC-1654' into 2.2 2012-02-20 10:34:27 +01:00
Benjamin Eberlei
19969e2d4b [DDC-1654] Add support for orphanRemoval on ManyToMany associations. This only makes sense when ManyToMany is used as uni-directional OneToMany association with join table. The join column has a unique constraint on it to enforce this on the DB level, but we dont validate that this actually happens. Foreign Key constraints help prevent issues and notify developers early if they use it wrong. 2012-02-20 10:33:43 +01:00
Benjamin Eberlei
573a7f81e1 Merge branch 'DDC-1659' into 2.2 2012-02-20 09:37:59 +01:00
Benjamin Eberlei
2460b3f115 [DDC-1659] Remove read only marker when clearing entities. 2012-02-20 09:37:11 +01:00
Benjamin Michotte
5f2fa7c08a Add fluent code for relations 2012-02-20 09:24:42 +01:00
Asmir Mustafic
a04df0622d nullable assoc 2012-02-20 00:32:30 +01:00
Benjamin Eberlei
b7e80decf3 Merge branch 'DDC-1651' into 2.2 2012-02-18 16:10:57 +01:00
Benjamin Eberlei
56d35f7932 [DDC-1651] Convert entities as parameters early in setParameter() to avoid them being part of result cache strings, which causes non-uniqueness. 2012-02-18 16:08:43 +01:00
Benjamin Eberlei
2116518096 Merge branch 'DDC-1643' into 2.2 2012-02-18 00:43:51 +01:00
Benjamin Eberlei
647bd2b2f2 [DDC-1643] Fix bugs when cloning PersistentCollection and re-using it. 2012-02-18 00:43:19 +01:00
Benjamin Eberlei
dadc85a650 Merge branch 'DDC-1655' into 2.2 2012-02-17 23:28:23 +01:00
Benjamin Eberlei
835943bd8d [DDC-1655][DDC-1650][DDC-1556] Fix issues with @postLoad Callback being not fired, or fired multiple times. 2012-02-17 23:27:35 +01:00
Benjamin Eberlei
39ff164c7e DDC-1641 - Fix test producing failure when skipped. 2012-02-10 21:38:42 +01:00
Benjamin Eberlei
5cdbc8ea6d Merge branch 'DDC-1634' into 2.2 2012-02-05 11:15:29 +01:00
Miha Vrhovnik
34ccde978a Proxy not initialized when parent has get<IDENTIFIER> function. Fixes DDC-1625 2012-02-05 11:14:55 +01:00
Benjamin Eberlei
5675e8cc8c Bump dev version to 2.2.1 2012-01-29 17:04:24 +01:00
37 changed files with 926 additions and 120 deletions

View File

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

View File

@@ -350,6 +350,7 @@
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>

View File

@@ -208,6 +208,7 @@ abstract class AbstractQuery
{
$key = trim($key, ':');
$value = $this->processParameterValue($value);
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
@@ -218,6 +219,53 @@ abstract class AbstractQuery
return $this;
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return $value;
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
return $this->convertObjectParameterToScalarValue($value);
default:
return $value;
}
}
protected function convertObjectParameterToScalarValue($value)
{
$class = $this->_em->getClassMetadata(get_class($value));
if ($class->isIdentifierComposite) {
throw new \InvalidArgumentException("Binding an entity with a composite primary key to a query is not supported. You should split the parameter into the explicit fields and bind them seperately.");
}
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
$values = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$values = $class->getIdentifierValues($value);
}
$value = $values[$class->getSingleIdentifierFieldName()];
if (!$value) {
throw new \InvalidArgumentException("Binding entities to query parameters only allowed for entities that have an identifier.");
}
return $value;
}
/**
* Sets a collection of query parameters.
*

View File

@@ -233,7 +233,7 @@ abstract class AbstractHydrator
if (isset($cache[$key]['isScalar'])) {
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;

View File

@@ -286,4 +286,4 @@ class ArrayHydrator extends AbstractHydrator
return $this->_ce[$className];
}
}
}

View File

@@ -234,20 +234,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_hints['fetchAlias'] = $dqlAlias;
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->_ce[$className]->lifecycleCallbacks[Events::postLoad])) {
$this->_ce[$className]->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
return $entity;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)

View File

@@ -130,17 +130,6 @@ class SimpleObjectHydrator extends AbstractHydrator
$uow = $this->_em->getUnitOfWork();
$entity = $uow->createEntity($entityName, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->class->lifecycleCallbacks[Events::postLoad])) {
$this->class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
$result[] = $entity;
}
@@ -191,4 +180,4 @@ class SimpleObjectHydrator extends AbstractHydrator
);
}
}
}
}

View File

@@ -1105,7 +1105,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
if ( ($mapping['type'] & (self::MANY_TO_ONE|self::MANY_TO_MANY)) > 0 &&
if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
isset($mapping['orphanRemoval']) &&
$mapping['orphanRemoval'] == true) {
@@ -1207,7 +1207,7 @@ class ClassMetadataInfo implements ClassMetadata
$uniqueContraintColumns = array();
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE) {
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
if (count($mapping['joinColumns']) == 1) {
$joinColumn['unique'] = true;
} else {
@@ -1336,6 +1336,8 @@ class ClassMetadataInfo implements ClassMetadata
}
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));

View File

@@ -142,8 +142,7 @@ class AnnotationDriver implements Driver
$classAnnotations = $this->_reader->getClassAnnotations($class);
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
$classAnnotations[get_class($annot)] = $annot;
}
@@ -176,17 +175,25 @@ class AnnotationDriver implements Driver
if ($tableAnnot->indexes !== null) {
foreach ($tableAnnot->indexes as $indexAnnot) {
$primaryTable['indexes'][$indexAnnot->name] = array(
'columns' => $indexAnnot->columns
);
$index = array('columns' => $indexAnnot->columns);
if ( ! empty($indexAnnot->name)) {
$primaryTable['indexes'][$indexAnnot->name] = $index;
} else {
$primaryTable['indexes'][] = $index;
}
}
}
if ($tableAnnot->uniqueConstraints !== null) {
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraint) {
$primaryTable['uniqueConstraints'][$uniqueConstraint->name] = array(
'columns' => $uniqueConstraint->columns
);
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
$uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns);
if ( ! empty($uniqueConstraintAnnot->name)) {
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
} else {
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
}
}
}
@@ -407,6 +414,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
@@ -424,8 +432,7 @@ class AnnotationDriver implements Driver
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->_reader->getMethodAnnotations($method);
// Compatibility with Doctrine Common 3.x
if ($annotations && is_int(key($annotations))) {
if ($annotations && is_numeric(key($annotations))) {
foreach ($annotations as $annot) {
$annotations[get_class($annot)] = $annot;
}
@@ -480,8 +487,7 @@ class AnnotationDriver implements Driver
{
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
if ($annot instanceof \Doctrine\ORM\Mapping\Entity) {
return false;

View File

@@ -397,6 +397,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']);
}
if (isset($manyToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphan-removal'];
}
if (isset($manyToManyElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$manyToManyElement['mapped-by'];
} else if (isset($manyToManyElement->{'join-table'})) {

View File

@@ -446,6 +446,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
$metadata->mapManyToMany($mapping);
}
}

View File

@@ -35,6 +35,8 @@ final class ManyToMany implements Annotation
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}

View File

@@ -305,6 +305,7 @@ final class PersistentCollection implements Collection
if ($this->association !== null &&
$this->association['isOwningSide'] &&
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
$this->owner &&
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
@@ -387,7 +388,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
@@ -425,7 +426,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] === ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -630,7 +631,7 @@ final class PersistentCollection implements Collection
$uow = $this->em->getUnitOfWork();
if ($this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
if ($this->association['type'] & ClassMetadata::TO_MANY && $this->association['orphanRemoval']) {
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
// hence for event listeners we need the objects in memory.
$this->initialize();
@@ -759,4 +760,27 @@ final class PersistentCollection implements Collection
return $this->coll->slice($offset, $length);
}
/**
* Cleanup internal state of cloned persistent collection.
*
* The following problems have to be prevented:
* 1. Added entities are added to old PC
* 2. New collection is not dirty, if reused on other entity nothing
* changes.
* 3. Snapshot leads to invalid diffs being generated.
* 4. Lazy loading grabs entities from old owner object.
* 5. New collection is connected to old owner and leads to duplicate keys.
*/
public function __clone()
{
$this->initialize();
$this->owner = null;
if (is_object($this->coll)) {
$this->coll = clone $this->coll;
}
$this->snapshot = array();
$this->changed();
}
}

View File

@@ -204,4 +204,4 @@ abstract class AbstractCollectionPersister
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
}

View File

@@ -698,16 +698,6 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
}
/**

View File

@@ -106,11 +106,16 @@ class ProxyFactory
* Generate the Proxy file name
*
* @param string $className
* @param string $baseDir Optional base directory for proxy file name generation.
* If not specified, the directory configured on the Configuration of the
* EntityManager will be used by this factory.
* @return string
*/
private function getProxyFileName($className)
private function getProxyFileName($className, $baseDir = null)
{
return $this->_proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
$proxyDir = $baseDir ?: $this->_proxyDir;
return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
}
/**
@@ -124,14 +129,16 @@ class ProxyFactory
public function generateProxyClasses(array $classes, $toDir = null)
{
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass) {
continue;
}
$proxyFileName = $this->getProxyFileName($class->name);
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
}
}
@@ -275,7 +282,7 @@ class ProxyFactory
);
if ($cheapCheck) {
$code = file($class->reflClass->getFileName());
$code = file($method->getDeclaringClass()->getFileName());
$code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1)));
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);

View File

@@ -282,7 +282,8 @@ final class Query extends AbstractQuery
}
$sqlPositions = $paramMappings[$key];
$value = array_values($this->processParameterValue($value));
// optimized multi value sql positions away for now, they are not allowed in DQL anyways.
$value = array($value);
$countValue = count($value);
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
@@ -305,39 +306,6 @@ final class Query extends AbstractQuery
return array($sqlParams, $types);
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
// TODO: What about Entities that have composite primary key?
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return array($value);
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
}
$class = $this->_em->getClassMetadata(get_class($value));
return array_values($class->getIdentifierValues($value));
default:
return array($value);
}
}
/**
* Defines a cache driver to be used for caching queries.
*

View File

@@ -636,11 +636,17 @@ class SqlWalker implements TreeWalker
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) continue;
if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
continue;
}
// Add foreign key columns of class and also parent classes
foreach ($class->associationMappings as $assoc) {
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
continue;
} else if ( !$addMetaColumns && !isset($assoc['id'])) {
continue;
}
$owningClass = (isset($assoc['inherited'])) ? $this->_em->getClassMetadata($assoc['inherited']) : $class;
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
@@ -654,6 +660,11 @@ class SqlWalker implements TreeWalker
}
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) {
continue;
}
// Add foreign key columns of subclasses
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);

View File

@@ -128,10 +128,12 @@ public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
* <description>
*
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>[] = $<variableName>;
<spaces>return $this;
}';
private static $_lifecycleCallbackMethodTemplate =
@@ -681,6 +683,9 @@ public function <methodName>()
private function _isAssociationIsNullable($associationMapping)
{
if (isset($associationMapping['id']) && $associationMapping['id']) {
return false;
}
if (isset($associationMapping['joinColumns'])) {
$joinColumns = $associationMapping['joinColumns'];
} else {

View File

@@ -106,6 +106,11 @@ class SchemaValidator
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) {
$ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " .
"the target entity '". $targetMetadata->name . "' also maps an association as identifier.";
}
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {

View File

@@ -584,6 +584,23 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->associationMappings[$propName];
// Persistent collection was exchanged with the "originally"
// created one. This can only mean it was cloned and replaced
// on another entity.
if ($actualValue instanceof PersistentCollection) {
$owner = $actualValue->getOwner();
if ($owner === null) { // cloned
$actualValue->setOwner($entity, $assoc);
} else if ($owner !== $entity) { // no clone, we have to fix
if (!$actualValue->isInitialized()) {
$actualValue->initialize(); // we have to do this otherwise the cols share state
}
$newValue = clone $actualValue;
$newValue->setOwner($entity, $assoc);
$class->reflFields[$propName]->setValue($entity, $newValue);
}
}
if ($orgValue instanceof PersistentCollection) {
// A PersistentCollection was de-referenced, so delete it.
$coid = spl_object_hash($orgValue);
@@ -620,6 +637,15 @@ class UnitOfWork implements PropertyChangedListener
foreach ($class->associationMappings as $field => $assoc) {
if (($val = $class->reflFields[$field]->getValue($entity)) !== null) {
$this->computeAssociationChanges($assoc, $val);
if (!isset($this->entityChangeSets[$oid]) &&
$assoc['isOwningSide'] &&
$assoc['type'] == ClassMetadata::MANY_TO_MANY &&
$val instanceof PersistentCollection &&
$val->isDirty()) {
$this->entityChangeSets[$oid] = array();
$this->originalEntityData[$oid] = $actualData;
$this->entityUpdates[$oid] = $entity;
}
}
}
}
@@ -1376,6 +1402,7 @@ class UnitOfWork implements PropertyChangedListener
if (isset($this->identityMap[$className][$idHash])) {
unset($this->identityMap[$className][$idHash]);
unset($this->readOnlyObjects[$oid]);
//$this->entityStates[$oid] = self::STATE_DETACHED;
@@ -2189,6 +2216,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionDeletions =
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
@@ -2498,6 +2526,17 @@ class UnitOfWork implements PropertyChangedListener
}
}
if ($overrideLocalValues) {
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
if ($this->evm->hasListeners(Events::postLoad)) {
$this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
}
}
return $entity;
}

View File

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

View File

@@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Navigation\NavCountry;
use Doctrine\Tests\Models\Navigation\NavPointOfInterest;
use Doctrine\Tests\Models\Navigation\NavTour;
use Doctrine\Tests\Models\Navigation\NavPhotos;
require_once __DIR__ . '/../../TestInit.php';
@@ -51,6 +52,25 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Brandenburger Tor', $poi->getName());
}
/**
* @group DDC-1651
*/
public function testSetParameterCompositeKeyObject()
{
$this->putGermanysBrandenburderTor();
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$photo = new NavPhotos($poi, "asdf");
$this->_em->persist($photo);
$this->_em->flush();
$this->_em->clear();
$dql = 'SELECT t FROM Doctrine\Tests\Models\Navigation\NavPhotos t WHERE t.poi = ?1';
$this->setExpectedException('Doctrine\ORM\Query\QueryException', 'A single-valued association path expression to an entity with a composite primary key is not supported.');
$sql = $this->_em->createQuery($dql)->getSQL();
}
public function testManyToManyCompositeRelation()
{
$this->putGermanysBrandenburderTor();
@@ -98,4 +118,4 @@ 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));
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* ManyToManyEventTest
*
* @author Francisco Facioni <fran6co@gmail.com>
*/
class ManyToManyEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var PostUpdateListener
*/
private $listener;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new PostUpdateListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::postUpdate, $this->listener);
}
public function testListenerShouldBeNotifiedOnlyWhenUpdating()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->assertFalse($this->listener->wasNotified);
$group = new CmsGroup();
$group->name = "admins";
$user->addGroup($group);
$this->_em->persist($user);
$this->_em->flush();
$this->assertTrue($this->listener->wasNotified);
}
/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'fran6co';
$user->name = 'Francisco Facioni';
$group = new CmsGroup();
$group->name = "users";
$user->addGroup($group);
return $user;
}
}
class PostUpdateListener
{
/**
* @var bool
*/
public $wasNotified = false;
/**
* @param $args
*/
public function postUpdate($args)
{
$this->wasNotified = true;
}
}

View File

@@ -599,4 +599,24 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(3, count($users));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
}
}
/**
* @group DDC-1651
*/
public function testSetParameterBindingSingleIdentifierObjectConverted()
{
$userC = new CmsUser;
$userC->name = 'Jonathan';
$userC->username = 'jwage';
$userC->status = 'developer';
$this->_em->persist($userC);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1");
$q->setParameter(1, $userC);
$this->assertEquals($userC->id, $q->getParameter(1));
}
}

View File

@@ -15,9 +15,12 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
} catch(\Exception $e) {
}
}
public function testReadOnlyEntityNeverChangeTracked()
@@ -36,6 +39,36 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->numericValue);
}
/**
* @group DDC-1659
*/
public function testClearReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear();
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
/**
* @group DDC-1659
*/
public function testClearEntitiesReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear(get_class($readOnly));
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
}
/**
@@ -58,4 +91,4 @@ class ReadOnlyEntity
$this->name = $name;
$this->numericValue = $number;
}
}
}

View File

@@ -6,6 +6,7 @@ use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Proxy\ProxyClassGenerator;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
use Doctrine\Tests\Models\Company\CompanyAuction;
require_once __DIR__ . '/../../TestInit.php';
@@ -39,6 +40,18 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
return $product->getId();
}
public function createAuction()
{
$event = new CompanyAuction();
$event->setData('Doctrine Cookbook');
$this->_em->persist($event);
$this->_em->flush();
$this->_em->clear();
return $event->getId();
}
public function testLazyLoadsFieldValuesFromDatabase()
{
$id = $this->createProduct();
@@ -161,6 +174,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
/**
* @group DDC-1625
*/
public function testDoNotInitializeProxyOnGettingTheIdentifier_DDC_1625()
{
$id = $this->createAuction();
/* @var $entity Doctrine\Tests\Models\Company\CompanyAuction */
$entity = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyAuction' , $id);
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals($id, $entity->getId());
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy when extending.");
}
public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType()
{
$product = new ECommerceProduct();

View File

@@ -433,4 +433,32 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($this->article1->id(), $refRep->source()->id());
$this->assertEquals($this->article2->id(), $refRep->target()->id());
}
/**
* @group DDC-1652
*/
public function testArrayHydrationWithCompositeKey()
{
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$before = count($this->_em->createQuery($dql)->getResult());
$this->article1 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id());
$this->article2 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article2->id());
$this->reference = new DDC117Reference($this->article2, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article1, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article2, $this->article2, "Test-Description");
$this->_em->persist($this->reference);
$this->_em->flush();
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$data = $this->_em->createQuery($dql)->getArrayResult();
$this->assertEquals($before + 3, count($data));
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
/**
* @group DDC-1643
*/
class DDC1643Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $user1;
private $user2;
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user1 = new CmsUser();
$user1->username = "beberlei";
$user1->name = "Benjamin";
$user1->status = "active";
$group1 = new CmsGroup();
$group1->name = "test";
$group2 = new CmsGroup();
$group2->name = "test";
$user1->addGroup($group1);
$user1->addGroup($group2);
$user2 = new CmsUser();
$user2->username = "romanb";
$user2->name = "Roman";
$user2->status = "active";
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($group1);
$this->_em->persist($group2);
$this->_em->flush();
$this->_em->clear();
$this->user1 = $this->_em->find(get_class($user1), $user1->id);
$this->user2 = $this->_em->find(get_class($user1), $user2->id);
}
public function testClonePersistentCollectionAndReuse()
{
$user1 = $this->user1;
$user1->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$this->assertEquals(2, count($user1->groups));
}
public function testClonePersistentCollectionAndShare()
{
$user1 = $this->user1;
$user2 = $this->user2;
$user2->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(2, count($user1->groups));
$this->assertEquals(2, count($user2->groups));
}
public function testCloneThenDirtyPersistentCollection()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = clone $user1->groups;
$user2->groups->add($group3);
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(2, count($user1->groups));
}
public function testNotCloneAndPassAroundFlush()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = $user1->groups;
$user2->groups->add($group3);
$this->assertEQuals(1, count($user1->groups->getInsertDiff()));
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(3, count($user1->groups));
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1654
*/
class DDC1654Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->setUpEntitySchema(array(
__NAMESPACE__ . '\\DDC1654Post',
__NAMESPACE__ . '\\DDC1654Comment',
));
}
public function testManyToManyRemoveFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->remove(0);
$post->comments->remove(1);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyRemoveElementFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->removeElement($post->comments[0]);
$post->comments->removeElement($post->comments[1]);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyClearCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->clear();
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
}
/**
* @Entity
*/
class DDC1654Post
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
/**
* @ManyToMany(targetEntity="DDC1654Comment", orphanRemoval=true,
* cascade={"persist"})
*/
public $comments = array();
}
/**
* @Entity
*/
class DDC1654Comment
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
}

View File

@@ -0,0 +1,144 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1655
* @group DDC-1640
* @group DDC-1556
*/
class DDC1655Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Bar'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Baz'),
));
} catch(\Exception $e) {
}
}
public function testPostLoadOneToManyInheritance()
{
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Foo');
$this->assertEquals(array("postLoad" => array("postLoad")), $cm->lifecycleCallbacks);
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Bar');
$this->assertEquals(array("postLoad" => array("postLoad", "postSubLoaded")), $cm->lifecycleCallbacks);
$baz = new DDC1655Baz();
$foo = new DDC1655Foo();
$foo->baz = $baz;
$bar = new DDC1655Bar();
$bar->baz = $baz;
$this->_em->persist($foo);
$this->_em->persist($bar);
$this->_em->persist($baz);
$this->_em->flush();
$this->_em->clear();
$baz = $this->_em->find(get_class($baz), $baz->id);
foreach ($baz->foos as $foo) {
$this->assertEquals(1, $foo->loaded, "should have loaded callback counter incremented for " . get_class($foo));
}
}
/**
* Check that post load is not executed several times when the entity
* is rehydrated again although its already known.
*/
public function testPostLoadInheritanceChild()
{
$bar = new DDC1655Bar();
$this->_em->persist($bar);
$this->_em->flush();
$this->_em->clear();
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$dql = "SELECT b FROM " . __NAMESPACE__ . "\DDC1655Bar b WHERE b.id = ?1";
$bar = $this->_em->createQuery($dql)->setParameter(1, $bar->id)->getSingleResult();
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$this->_em->refresh($bar);
$this->assertEquals(2, $bar->loaded);
$this->assertEquals(2, $bar->subLoaded);
}
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({
* "foo" = "DDC1655Foo",
* "bar" = "DDC1655Bar"
* })
* @HasLifecycleCallbacks
*/
class DDC1655Foo
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
public $loaded = 0;
/**
* @ManyToOne(targetEntity="DDC1655Baz", inversedBy="foos")
*/
public $baz;
/**
* @PostLoad
*/
public function postLoad()
{
$this->loaded++;
}
}
/**
* @Entity
* @HasLifecycleCallbacks
*/
class DDC1655Bar extends DDC1655Foo
{
public $subLoaded;
/**
* @PostLoad
*/
public function postSubLoaded()
{
$this->subLoaded++;
}
}
/**
* @Entity
*/
class DDC1655Baz
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/**
* @OneToMany(targetEntity="DDC1655Foo", mappedBy="baz")
*/
public $foos = array();
}

View File

@@ -377,6 +377,7 @@ class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
array(
'user_id' => 'id',
),
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}

View File

@@ -1059,7 +1059,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$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"
"SELECT d0_.article_id AS article_id0, d0_.title AS title1, d1_.article_id AS article_id2, d1_.title AS title3, d2_.source_id AS source_id4, d2_.target_id AS target_id5 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

@@ -109,6 +109,33 @@ class SchemaValidatorTest extends \Doctrine\Tests\OrmTestCase
$ce
);
}
/**
* @group DDC-1587
*/
public function testValidOneToOneAsIdentifierSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1587ValidEntity2');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1587ValidEntity1');
$ce = $this->validator->validateClass($class1);
$this->assertEquals(array(), $ce);
}
/**
* @group DDC-1649
*/
public function testInvalidTripleAssociationAsKeyMapping()
{
$classThree = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1649Three');
$ce = $this->validator->validateClass($classThree);
$this->assertEquals(Array(
"Cannot map association 'Doctrine\Tests\ORM\Tools\DDC1649Three#two as identifier, because the target entity 'Doctrine\Tests\ORM\Tools\DDC1649Two' also maps an association as identifier.",
"The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\Tests\ORM\Tools\DDC1649Two'."
), $ce);
}
}
/**
@@ -154,3 +181,87 @@ class InvalidEntity2
*/
protected $assoc;
}
/**
* @Entity(repositoryClass="Entity\Repository\Agent")
* @Table(name="agent")
*/
class DDC1587ValidEntity1
{
/**
* @var int
*
* @Id @GeneratedValue
* @Column(name="pk", type="integer")
*/
private $pk;
/**
* @var string
*
* @Column(name="name", type="string", length=32)
*/
private $name;
/**
* @var Identifier
*
* @OneToOne(targetEntity="DDC1587ValidEntity2", cascade={"all"}, mappedBy="agent")
* @JoinColumn(name="pk", referencedColumnName="pk_agent")
*/
private $identifier;
}
/**
* @Entity
* @Table
*/
class DDC1587ValidEntity2
{
/**
* @var DDC1587ValidEntity1
*
* @Id
* @OneToOne(targetEntity="DDC1587ValidEntity1", inversedBy="identifier")
* @JoinColumn(name="pk_agent", referencedColumnName="pk", nullable=false)
*/
private $agent;
/**
* @var string
*
* @Column(name="num", type="string", length=16, nullable=true)
*/
private $num;
}
/**
* @Entity
*/
class DDC1649One
{
/**
* @Id @Column @GeneratedValue
*/
public $id;
}
/**
* @Entity
*/
class DDC1649Two
{
/** @Id @ManyToOne(targetEntity="DDC1649One")@JoinColumn(name="id", referencedColumnName="id") */
public $one;
}
/**
* @Entity
*/
class DDC1649Three
{
/** @Id @ManyToOne(targetEntity="DDC1649Two") @JoinColumn(name="id",
* referencedColumnName="id") */
private $two;
}

View File

@@ -22,6 +22,21 @@ class SetupTest extends \Doctrine\Tests\OrmTestCase
$this->originalIncludePath = get_include_path();
}
public function tearDown()
{
if ( ! $this->originalIncludePath) {
return;
}
set_include_path($this->originalIncludePath);
$loaders = spl_autoload_functions();
for ($i = 0; $i < count($loaders); $i++) {
if ($i > $this->originalAutoloaderCount+1) {
spl_autoload_unregister($loaders[$i]);
}
}
}
public function testGitAutoload()
{
Setup::registerAutoloadGit(__DIR__ . "/../../../../../");
@@ -92,15 +107,4 @@ class SetupTest extends \Doctrine\Tests\OrmTestCase
$this->assertSame($cache, $config->getMetadataCacheImpl());
$this->assertSame($cache, $config->getQueryCacheImpl());
}
public function tearDown()
{
set_include_path($this->originalIncludePath);
$loaders = spl_autoload_functions();
for ($i = 0; $i < count($loaders); $i++) {
if ($i > $this->originalAutoloaderCount+1) {
spl_autoload_unregister($loaders[$i]);
}
}
}
}
}

View File

@@ -38,6 +38,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
/** Whether the database schema has already been created. */
protected static $_tablesCreated = array();
/**
* Array of entity class name to their tables that were created.
* @var array
*/
protected static $_entityTablesCreated = array();
/** List of model sets and their classes. */
protected static $_modelSets = array(
'cms' => array(
@@ -235,6 +241,25 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$this->_em->clear();
}
protected function setUpEntitySchema(array $classNames)
{
if ($this->_em === null) {
throw new \RuntimeException("EntityManager not set, you have to call parent::setUp() before invoking this method.");
}
$classes = array();
foreach ($classNames as $className) {
if ( ! isset(static::$_entityTablesCreated[$className])) {
static::$_entityTablesCreated[$className] = true;
$classes[] = $this->_em->getClassMetadata($className);
}
}
if ($classes) {
$this->_schemaTool->createSchema($classes);
}
}
/**
* Creates a connection to the test database, if there is none yet, and
* creates the necessary tables.