mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e9438ba60 | ||
|
|
73483bf003 | ||
|
|
245c30a1a0 | ||
|
|
15dc64f2f9 | ||
|
|
66951f3c47 | ||
|
|
5f21f177e9 | ||
|
|
7af877d17e | ||
|
|
f628a7f609 | ||
|
|
64c222e4d3 | ||
|
|
13db27609c | ||
|
|
f039902a44 | ||
|
|
59d0c02aa9 | ||
|
|
63d1d847ac | ||
|
|
ce4aacaaee | ||
|
|
dadcc7e9fc | ||
|
|
cc9b96e259 | ||
|
|
006412bad9 | ||
|
|
ffbaaece93 | ||
|
|
c3fa29f298 | ||
|
|
aa82a75726 | ||
|
|
fba85d481f | ||
|
|
5bdfad9e0a | ||
|
|
d69a79c723 | ||
|
|
f51409b627 | ||
|
|
8c8deb9c9b | ||
|
|
79c8f42483 | ||
|
|
04ae8f2c64 | ||
|
|
ecb495d293 | ||
|
|
1418173870 | ||
|
|
26a6b69993 | ||
|
|
fa5ee57faf | ||
|
|
2a2f010788 | ||
|
|
e958085559 | ||
|
|
98ba0c1ab2 | ||
|
|
b574be5e3b | ||
|
|
79ad9068e1 | ||
|
|
af403d52fb | ||
|
|
78fa740b94 | ||
|
|
715fdd7559 | ||
|
|
884ef42075 | ||
|
|
5d3a626c2b | ||
|
|
587dda90d3 | ||
|
|
21ae0d2a45 | ||
|
|
f4dc533127 | ||
|
|
c9d1b3a428 | ||
|
|
9170e1b810 | ||
|
|
f1d2a79a87 | ||
|
|
dd2f3967cd | ||
|
|
4100a6a7a3 | ||
|
|
bfb590459e | ||
|
|
191520439b | ||
|
|
31709b4c1a | ||
|
|
af6de10c5b | ||
|
|
f4a78e13ee | ||
|
|
bd37b83f4f | ||
|
|
19969e2d4b | ||
|
|
573a7f81e1 | ||
|
|
2460b3f115 | ||
|
|
5f2fa7c08a | ||
|
|
a04df0622d | ||
|
|
b7e80decf3 | ||
|
|
56d35f7932 | ||
|
|
2116518096 | ||
|
|
647bd2b2f2 | ||
|
|
dadc85a650 | ||
|
|
835943bd8d | ||
|
|
39ff164c7e | ||
|
|
5cdbc8ea6d | ||
|
|
34ccde978a | ||
|
|
5675e8cc8c |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library","version":"2.2.0",
|
||||
"type": "library","version":"2.2.2",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\DBAL\Cache\QueryCacheProfile,
|
||||
Doctrine\ORM\Query\QueryException,
|
||||
Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
Doctrine\ORM\Internal\Hydration\CacheHydrator;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
@@ -29,7 +30,6 @@ use Doctrine\DBAL\Types\Type,
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
@@ -101,6 +101,11 @@ abstract class AbstractQuery
|
||||
*/
|
||||
protected $_expireResultCache = false;
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||
*/
|
||||
protected $_hydrationCacheProfile;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
||||
*
|
||||
@@ -208,6 +213,7 @@ abstract class AbstractQuery
|
||||
{
|
||||
$key = trim($key, ':');
|
||||
|
||||
$value = $this->processParameterValue($value);
|
||||
if ($type === null) {
|
||||
$type = Query\ParameterTypeInferer::inferType($value);
|
||||
}
|
||||
@@ -218,6 +224,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.
|
||||
*
|
||||
@@ -247,6 +300,68 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cache profile for hydration caching.
|
||||
*
|
||||
* If no result cache driver is set in the QueryCacheProfile, the default
|
||||
* result cache driver is used from the configuration.
|
||||
*
|
||||
* Important: Hydration caching does NOT register entities in the
|
||||
* UnitOfWork when retrieved from the cache. Never use result cached
|
||||
* entities for requests that also flush the EntityManager. If you want
|
||||
* some form of caching with UnitOfWork registration you should use
|
||||
* {@see AbstractQuery::setResultCacheProfile()}.
|
||||
*
|
||||
* @example
|
||||
* $lifetime = 100;
|
||||
* $resultKey = "abc";
|
||||
* $query->setHydrationCacheProfile(new QueryCacheProfile());
|
||||
* $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
|
||||
*
|
||||
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
|
||||
* @return \Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
|
||||
{
|
||||
if ( ! $profile->getResultCacheDriver()) {
|
||||
$resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
|
||||
$profile = $profile->setResultCacheDriver($resultCacheDriver);
|
||||
}
|
||||
|
||||
$this->_hydrationCacheProfile = $profile;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||
*/
|
||||
public function getHydrationCacheProfile()
|
||||
{
|
||||
return $this->_hydrationCacheProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cache profile for the result cache.
|
||||
*
|
||||
* If no result cache driver is set in the QueryCacheProfile, the default
|
||||
* result cache driver is used from the configuration.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
|
||||
* @return \Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setResultCacheProfile(QueryCacheProfile $profile = null)
|
||||
{
|
||||
if ( ! $profile->getResultCacheDriver()) {
|
||||
$resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
|
||||
$profile = $profile->setResultCacheDriver($resultCacheDriver);
|
||||
}
|
||||
|
||||
$this->_queryCacheProfile = $profile;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cache driver to be used for caching result sets and implictly enables caching.
|
||||
*
|
||||
@@ -592,15 +707,68 @@ abstract class AbstractQuery
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
$setCacheEntry = function() {};
|
||||
|
||||
if ($this->_hydrationCacheProfile !== null) {
|
||||
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
|
||||
|
||||
$queryCacheProfile = $this->getHydrationCacheProfile();
|
||||
$cache = $queryCacheProfile->getResultCacheDriver();
|
||||
$result = $cache->fetch($cacheKey);
|
||||
|
||||
if (isset($result[$realCacheKey])) {
|
||||
return $result[$realCacheKey];
|
||||
}
|
||||
|
||||
if ( ! $result) {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
|
||||
$result[$realCacheKey] = $data;
|
||||
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
|
||||
};
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
if (is_numeric($stmt)) {
|
||||
$setCacheEntry($stmt);
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
$setCacheEntry($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result cache id to use to store the result set cache entry.
|
||||
* Will return the configured id if it exists otherwise a hash will be
|
||||
* automatically generated for you.
|
||||
*
|
||||
* @return array ($key, $hash)
|
||||
*/
|
||||
protected function getHydrationCacheId()
|
||||
{
|
||||
$params = $this->getParameters();
|
||||
|
||||
foreach ($params AS $key => $value) {
|
||||
$params[$key] = $this->processParameterValue($value);
|
||||
}
|
||||
|
||||
$sql = $this->getSQL();
|
||||
$queryCacheProfile = $this->getHydrationCacheProfile();
|
||||
$hints = $this->getHints();
|
||||
$hints['hydrationMode'] = $this->getHydrationMode();
|
||||
ksort($hints);
|
||||
|
||||
return $queryCacheProfile->generateCacheKeys($sql, $params, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -230,6 +230,28 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
$this->_attributes['queryCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the hydration cache (SQL cache).
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getHydrationCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['hydrationCacheImpl'])
|
||||
? $this->_attributes['hydrationCacheImpl']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for the hydration cache (SQL cache).
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setHydrationCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['hydrationCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -286,4 +286,4 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +1207,11 @@ 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;
|
||||
if (! isset($mapping['id']) || ! $mapping['id']) {
|
||||
$joinColumn['unique'] = true;
|
||||
}
|
||||
} else {
|
||||
$uniqueContraintColumns[] = $joinColumn['name'];
|
||||
}
|
||||
@@ -1336,6 +1338,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']));
|
||||
@@ -1989,7 +1993,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
if ( ! class_exists($className)) {
|
||||
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
|
||||
}
|
||||
if (is_subclass_of($className, $this->name)) {
|
||||
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
|
||||
$this->subClasses[] = $className;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -130,6 +130,13 @@ class DatabaseDriver implements Driver
|
||||
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
||||
}
|
||||
|
||||
if ( ! $table->hasPrimaryKey()) {
|
||||
throw new MappingException(
|
||||
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
|
||||
"support reverse engineering from tables that don't have a primary key."
|
||||
);
|
||||
}
|
||||
|
||||
$pkColumns = $table->getPrimaryKey()->getColumns();
|
||||
sort($pkColumns);
|
||||
sort($allForeignKeyColumns);
|
||||
@@ -185,12 +192,13 @@ class DatabaseDriver implements Driver
|
||||
$fieldMappings = array();
|
||||
foreach ($columns as $column) {
|
||||
$fieldMapping = array();
|
||||
if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
|
||||
$fieldMapping['id'] = true;
|
||||
} else if (in_array($column->getName(), $allForeignKeyColumns)) {
|
||||
|
||||
if (in_array($column->getName(), $allForeignKeyColumns)) {
|
||||
continue;
|
||||
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
|
||||
$fieldMapping['id'] = true;
|
||||
}
|
||||
|
||||
|
||||
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
|
||||
$fieldMapping['columnName'] = $column->getName();
|
||||
$fieldMapping['type'] = strtolower((string) $column->getType());
|
||||
@@ -291,13 +299,23 @@ class DatabaseDriver implements Driver
|
||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
|
||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
|
||||
|
||||
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
|
||||
$associationMapping['id'] = true;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($cols); $i++) {
|
||||
$associationMapping['joinColumns'][] = array(
|
||||
'name' => $cols[$i],
|
||||
'referencedColumnName' => $fkCols[$i],
|
||||
);
|
||||
}
|
||||
$metadata->mapManyToOne($associationMapping);
|
||||
|
||||
//Here we need to check if $cols are the same as $primaryKeyColums
|
||||
if (!array_diff($cols,$primaryKeyColumns)) {
|
||||
$metadata->mapOneToOne($associationMapping);
|
||||
} else {
|
||||
$metadata->mapManyToOne($associationMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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'})) {
|
||||
|
||||
@@ -402,9 +402,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
if (isset($manyToManyElement['mappedBy'])) {
|
||||
$mapping['mappedBy'] = $manyToManyElement['mappedBy'];
|
||||
} else if (isset($manyToManyElement['joinTable'])) {
|
||||
if (isset($manyToManyElement['inversedBy'])) {
|
||||
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
|
||||
}
|
||||
|
||||
$joinTableElement = $manyToManyElement['joinTable'];
|
||||
$joinTable = array(
|
||||
@@ -434,6 +431,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['joinTable'] = $joinTable;
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['inversedBy'])) {
|
||||
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['cascade'])) {
|
||||
$mapping['cascade'] = $manyToManyElement['cascade'];
|
||||
}
|
||||
@@ -446,6 +447,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['indexBy'] = $manyToManyElement['indexBy'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,4 +204,4 @@ abstract class AbstractCollectionPersister
|
||||
* @param mixed $element
|
||||
*/
|
||||
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1153,6 +1143,7 @@ class BasicEntityPersister
|
||||
$placeholder = '?';
|
||||
|
||||
if (isset($this->_columnTypes[$column]) &&
|
||||
isset($this->_class->fieldNames[$column]) &&
|
||||
isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) {
|
||||
$type = Type::getType($this->_columnTypes[$column]);
|
||||
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
|
||||
|
||||
78
lib/Doctrine/ORM/Proxy/Autoloader.php
Normal file
78
lib/Doctrine/ORM/Proxy/Autoloader.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Proxy;
|
||||
|
||||
/**
|
||||
* Special Autoloader for Proxy classes because them not being PSR-0 compatible.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
/**
|
||||
* Resolve proxy class name to a filename based on the following pattern.
|
||||
*
|
||||
* 1. Remove Proxy namespace from class name
|
||||
* 2. Remove namespace seperators from remaining class name.
|
||||
* 3. Return PHP filename from proxy-dir with the result from 2.
|
||||
*
|
||||
* @param string $proxyDir
|
||||
* @param string $proxyNamespace
|
||||
* @param string $className
|
||||
* @return string
|
||||
*/
|
||||
static public function resolveFile($proxyDir, $proxyNamespace, $className)
|
||||
{
|
||||
if (0 !== strpos($className, $proxyNamespace)) {
|
||||
throw ProxyException::notProxyClass($className, $proxyNamespace);
|
||||
}
|
||||
|
||||
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1));
|
||||
return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register and return autoloader callback for the given proxy dir and
|
||||
* namespace.
|
||||
*
|
||||
* @param string $proxyDir
|
||||
* @param string $proxyNamespace
|
||||
* @param Closure $notFoundCallback Invoked when the proxy file is not found.
|
||||
* @return Closure
|
||||
*/
|
||||
static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null)
|
||||
{
|
||||
$proxyNamespace = ltrim($proxyNamespace, "\\");
|
||||
$autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) {
|
||||
if (0 === strpos($className, $proxyNamespace)) {
|
||||
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
|
||||
|
||||
if ($notFoundCallback && ! file_exists($file)) {
|
||||
$notFoundCallback($proxyDir, $proxyNamespace, $className);
|
||||
}
|
||||
|
||||
require $file;
|
||||
}
|
||||
};
|
||||
spl_autoload_register($autoloader);
|
||||
return $autoloader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,4 +40,12 @@ class ProxyException extends \Doctrine\ORM\ORMException {
|
||||
return new self("You must configure a proxy namespace. See docs for details");
|
||||
}
|
||||
|
||||
}
|
||||
public static function notProxyClass($className, $proxyNamespace)
|
||||
{
|
||||
return new self(sprintf(
|
||||
"The class %s is not part of the proxy namespace %s",
|
||||
$className, $proxyNamespace
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,20 +125,27 @@ class ProxyFactory
|
||||
* @param string $toDir The target directory of the proxy classes. If not specified, the
|
||||
* directory configured on the Configuration of the EntityManager used
|
||||
* by this factory is used.
|
||||
* @return int Number of generated proxies.
|
||||
*/
|
||||
public function generateProxyClasses(array $classes, $toDir = null)
|
||||
{
|
||||
$proxyDir = $toDir ?: $this->_proxyDir;
|
||||
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
|
||||
$num = 0;
|
||||
|
||||
foreach ($classes as $class) {
|
||||
/* @var $class ClassMetadata */
|
||||
if ($class->isMappedSuperclass) {
|
||||
if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$proxyFileName = $this->getProxyFileName($class->name);
|
||||
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
|
||||
|
||||
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
|
||||
$num++;
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,7 +287,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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -562,6 +562,8 @@ class Expr
|
||||
{
|
||||
if (is_numeric($literal) && !is_string($literal)) {
|
||||
return (string) $literal;
|
||||
} else if (is_bool($literal)) {
|
||||
return $literal ? "true" : "false";
|
||||
} else {
|
||||
return "'" . str_replace("'", "''", $literal) . "'";
|
||||
}
|
||||
|
||||
@@ -440,13 +440,15 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' ' . $this->_platform->getWriteLockSQL();
|
||||
break;
|
||||
|
||||
case LockMode::PESSIMISTIC_OPTIMISTIC:
|
||||
case LockMode::OPTIMISTIC:
|
||||
foreach ($this->_selectedClasses AS $selectedClass) {
|
||||
if ( ! $class->isVersioned) {
|
||||
if ( ! $selectedClass['class']->isVersioned) {
|
||||
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LockMode::NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
|
||||
@@ -636,11 +638,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 +662,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);
|
||||
@@ -1090,7 +1103,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
|
||||
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
|
||||
|
||||
$col = $sqlTableAlias . '.' . $columnName;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -857,6 +862,14 @@ public function <methodName>()
|
||||
|
||||
if ($this->_generateAnnotations) {
|
||||
$lines[] = $this->_spaces . ' *';
|
||||
|
||||
if (isset($associationMapping['id']) && $associationMapping['id']) {
|
||||
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
|
||||
|
||||
if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
|
||||
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
|
||||
}
|
||||
}
|
||||
|
||||
$type = null;
|
||||
switch ($associationMapping['type']) {
|
||||
|
||||
@@ -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'])) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.2.0';
|
||||
const VERSION = '2.2.2';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
||||
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
Submodule lib/vendor/doctrine-common updated: 84838bb8c9...14eb4d62b6
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
Submodule lib/vendor/doctrine-dbal updated: e01f7468d8...ee8f64111d
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php
Normal file
86
tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use Doctrine\Tests\Models\Cms\CmsUser;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
|
||||
/**
|
||||
* @group DDC-1766
|
||||
*/
|
||||
class HydrationCacheTest extends OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
|
||||
$user = new CmsUser;
|
||||
$user->name = "Benjamin";
|
||||
$user->username = "beberlei";
|
||||
$user->status = 'active';
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testHydrationCache()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u";
|
||||
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
|
||||
->getResult();
|
||||
|
||||
$c = $this->getCurrentQueryCount();
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
|
||||
->getResult();
|
||||
|
||||
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
|
||||
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
|
||||
->getArrayResult();
|
||||
|
||||
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration is part of cache key.");
|
||||
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
|
||||
->getArrayResult();
|
||||
|
||||
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached");
|
||||
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
|
||||
->getArrayResult();
|
||||
|
||||
$this->assertTrue($cache->contains('cachekey'), 'Explicit cache key');
|
||||
|
||||
$users = $this->_em->createQuery($dql)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
|
||||
->getArrayResult();
|
||||
$this->assertEquals($c + 2, $this->getCurrentQueryCount(), "Hydration now cached");
|
||||
}
|
||||
|
||||
public function testHydrationParametersSerialization()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$user = new CmsUser();
|
||||
$user->id = 1;
|
||||
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1";
|
||||
$query = $this->_em->createQuery($dql)
|
||||
->setParameter(1, $user)
|
||||
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
|
||||
|
||||
$query->getResult();
|
||||
$c = $this->getCurrentQueryCount();
|
||||
$query->getResult();
|
||||
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ use Doctrine\Tests\Models\CMS\CmsArticle,
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group locking
|
||||
*/
|
||||
class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
|
||||
protected function setUp() {
|
||||
$this->useModelSet('cms');
|
||||
@@ -139,7 +142,6 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
|
||||
|
||||
/**
|
||||
* @group DDC-178
|
||||
* @group locking
|
||||
*/
|
||||
public function testLockPessimisticRead() {
|
||||
$readLockSql = $this->_em->getConnection()->getDatabasePlatform()->getReadLockSql();
|
||||
@@ -166,4 +168,17 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
|
||||
$query = array_pop( $this->_sqlLoggerStack->queries );
|
||||
$this->assertContains($readLockSql, $query['sql']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1693
|
||||
*/
|
||||
public function testLockOptimisticNonVersionedThrowsExceptionInDQL()
|
||||
{
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'";
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock on an entity failed.');
|
||||
$sql = $this->_em->createQuery($dql)->setHint(
|
||||
\Doctrine\ORM\Query::HINT_LOCK_MODE, \Doctrine\DBAL\LockMode::OPTIMISTIC
|
||||
)->getSQL();
|
||||
}
|
||||
}
|
||||
|
||||
76
tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php
Normal file
76
tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1643Test.php
Normal file
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1643Test.php
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
103
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1654Test.php
Normal file
103
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1654Test.php
Normal 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;
|
||||
}
|
||||
144
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1655Test.php
Normal file
144
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1655Test.php
Normal 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();
|
||||
}
|
||||
158
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1695Test.php
Normal file
158
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1695Test.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
/**
|
||||
* @group DDC-1695
|
||||
*/
|
||||
class DDC1695Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function testIssue()
|
||||
{
|
||||
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != "sqlite") {
|
||||
$this->markTestSkipped("Only with sqlite");
|
||||
}
|
||||
$dql = "SELECT n.smallText, n.publishDate FROM " . __NAMESPACE__ . "\\DDC1695News n";
|
||||
$sql = $this->_em->createQuery($dql)->getSQL();
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT d0_."SmallText" AS SmallText0, d0_."PublishDate" AS PublishDate1 FROM "DDC1695News" d0_',
|
||||
$sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Table(name="`DDC1695News`")
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1695News
|
||||
{
|
||||
/**
|
||||
* @var integer $idNews
|
||||
*
|
||||
* @Column(name="`IdNews`", type="integer", nullable=false)
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private $idNews;
|
||||
|
||||
/**
|
||||
* @var bigint $iduser
|
||||
*
|
||||
* @Column(name="`IdUser`", type="bigint", nullable=false)
|
||||
*/
|
||||
private $idUser;
|
||||
|
||||
/**
|
||||
* @var integer $idLanguage
|
||||
*
|
||||
* @Column(name="`IdLanguage`", type="integer", nullable=false)
|
||||
*/
|
||||
private $idLanguage;
|
||||
|
||||
/**
|
||||
* @var integer $idCondition
|
||||
*
|
||||
* @Column(name="`IdCondition`", type="integer", nullable=true)
|
||||
*/
|
||||
private $idCondition;
|
||||
|
||||
/**
|
||||
* @var integer $idHealthProvider
|
||||
*
|
||||
* @Column(name="`IdHealthProvider`", type="integer", nullable=true)
|
||||
*/
|
||||
private $idHealthProvider;
|
||||
|
||||
/**
|
||||
* @var integer $idSpeciality
|
||||
*
|
||||
* @Column(name="`IdSpeciality`", type="integer", nullable=true)
|
||||
*/
|
||||
private $idSpeciality;
|
||||
|
||||
/**
|
||||
* @var integer $idMedicineType
|
||||
*
|
||||
* @Column(name="`IdMedicineType`", type="integer", nullable=true)
|
||||
*/
|
||||
private $idMedicineType;
|
||||
|
||||
/**
|
||||
* @var integer $idTreatment
|
||||
*
|
||||
* @Column(name="`IdTreatment`", type="integer", nullable=true)
|
||||
*/
|
||||
private $idTreatment;
|
||||
|
||||
/**
|
||||
* @var string $title
|
||||
*
|
||||
* @Column(name="`Title`", type="string", nullable=true)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string $smallText
|
||||
*
|
||||
* @Column(name="`SmallText`", type="string", nullable=true)
|
||||
*/
|
||||
private $smallText;
|
||||
|
||||
/**
|
||||
* @var string $longText
|
||||
*
|
||||
* @Column(name="`LongText`", type="string", nullable=true)
|
||||
*/
|
||||
private $longText;
|
||||
|
||||
/**
|
||||
* @var datetimetz $publishDate
|
||||
*
|
||||
* @Column(name="`PublishDate`", type="datetimetz", nullable=true)
|
||||
*/
|
||||
private $publishDate;
|
||||
|
||||
/**
|
||||
* @var tsvector $idxNews
|
||||
*
|
||||
* @Column(name="`IdxNews`", type="tsvector", nullable=true)
|
||||
*/
|
||||
private $idxNews;
|
||||
|
||||
/**
|
||||
* @var boolean $highlight
|
||||
*
|
||||
* @Column(name="`Highlight`", type="boolean", nullable=false)
|
||||
*/
|
||||
private $highlight;
|
||||
|
||||
/**
|
||||
* @var integer $order
|
||||
*
|
||||
* @Column(name="`Order`", type="integer", nullable=false)
|
||||
*/
|
||||
private $order;
|
||||
|
||||
/**
|
||||
* @var boolean $deleted
|
||||
*
|
||||
* @Column(name="`Deleted`", type="boolean", nullable=false)
|
||||
*/
|
||||
private $deleted;
|
||||
|
||||
/**
|
||||
* @var boolean $active
|
||||
*
|
||||
* @Column(name="`Active`", type="boolean", nullable=false)
|
||||
*/
|
||||
private $active;
|
||||
|
||||
/**
|
||||
* @var boolean $updateToHighlighted
|
||||
*
|
||||
* @Column(name="`UpdateToHighlighted`", type="boolean", nullable=true)
|
||||
*/
|
||||
private $updateToHighlighted;
|
||||
}
|
||||
@@ -377,6 +377,7 @@ class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
array(
|
||||
'user_id' => 'id',
|
||||
),
|
||||
'orphanRemoval' => false,
|
||||
),
|
||||
), $this->cm->associationMappings);
|
||||
}
|
||||
|
||||
62
tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php
Normal file
62
tests/Doctrine/Tests/ORM/Proxy/AutoloaderTest.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\ORM\Proxy;
|
||||
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use Doctrine\ORM\Proxy\Autoloader;
|
||||
|
||||
/**
|
||||
* @group DDC-1698
|
||||
*/
|
||||
class AutoloaderTest extends OrmTestCase
|
||||
{
|
||||
static public function dataResolveFile()
|
||||
{
|
||||
return array(
|
||||
array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
|
||||
array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
|
||||
array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp/__CG__OtherRealClass.php'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataResolveFile
|
||||
*/
|
||||
public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile)
|
||||
{
|
||||
$actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
|
||||
$this->assertEquals($expectedProxyFile, $actualProxyFile);
|
||||
}
|
||||
|
||||
public function testAutoload()
|
||||
{
|
||||
if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) {
|
||||
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
|
||||
}
|
||||
|
||||
$autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) {
|
||||
file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ");
|
||||
});
|
||||
|
||||
$this->assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true));
|
||||
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,20 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertEquals(1, substr_count($classCode, 'function __sleep'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1771
|
||||
*/
|
||||
public function testSkipAbstractClassesOnGeneration()
|
||||
{
|
||||
$cm = new \Doctrine\ORM\Mapping\ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
|
||||
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
$this->assertNotNull($cm->reflClass);
|
||||
|
||||
$num = $this->_proxyFactory->generateProxyClasses(array($cm));
|
||||
|
||||
$this->assertEquals(0, $num, "No proxies generated.");
|
||||
}
|
||||
|
||||
public function testNoConfigDir_ThrowsException()
|
||||
{
|
||||
$this->setExpectedException('Doctrine\ORM\Proxy\ProxyException');
|
||||
@@ -183,3 +197,8 @@ class SleepClass
|
||||
return array('id');
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractClass
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -336,4 +336,13 @@ class ExprTest extends \Doctrine\Tests\OrmTestCase
|
||||
$orExpr = $this->_expr->orx();
|
||||
$orExpr->add($this->_expr->quot(5, 2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1683
|
||||
*/
|
||||
public function testBooleanLiteral()
|
||||
{
|
||||
$this->assertEquals('true', $this->_expr->literal(true));
|
||||
$this->assertEquals('false', $this->_expr->literal(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,6 +893,20 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1693
|
||||
* @group locking
|
||||
*/
|
||||
public function testLockModeNoneQueryHint()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ".
|
||||
"FROM cms_users c0_ WHERE c0_.username = 'gblanco'",
|
||||
array(Query::HINT_LOCK_MODE => \Doctrine\DBAL\LockMode::NONE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-430
|
||||
*/
|
||||
@@ -1059,7 +1073,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"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user