mirror of
https://github.com/doctrine/orm.git
synced 2026-04-24 06:58:19 +02:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73e4be7c7b | |||
| d7026c46ec | |||
| b7bfbb6adb | |||
| c9161fcd6f | |||
| 147f8fffff | |||
| e73428a051 | |||
| a3d93afc4f | |||
| b0e4e3eda4 | |||
| 95dcf51ad5 | |||
| 5365a418e9 | |||
| 47ce079b64 | |||
| 8e95672f49 | |||
| e16de704a1 | |||
| 3e57c46afd | |||
| 5e702ad524 | |||
| 9e9864c684 | |||
| 88567ea395 | |||
| bf18aac62d | |||
| 4d16e30a16 | |||
| d592c14a6c | |||
| da41161d73 | |||
| 33e23cdddb | |||
| c47c23a101 | |||
| 7352b97b14 | |||
| dce0aeaa15 | |||
| a2c23fb9cb | |||
| aa6dc9695d | |||
| c4a2a348f4 | |||
| 6ab27993fc | |||
| 5eedccc22a | |||
| 592122fbcb | |||
| aa5820309e | |||
| b183818fa8 | |||
| 2af84c6025 | |||
| f181cf6c6b | |||
| 62431ae477 | |||
| 0d93461e66 | |||
| a5eb0f2e82 | |||
| 9a393ccba7 | |||
| 224ac9725e | |||
| 0af9ee0140 | |||
| fecadf059c | |||
| 800215040a | |||
| ec4dd4ab44 | |||
| 7378035f68 | |||
| 7fbcbfa271 | |||
| 4a38c96ec5 | |||
| 110d771883 | |||
| 996c5048ab | |||
| cd746beae2 | |||
| dfbc6bbea3 | |||
| c4209b4654 | |||
| 6279c80e05 | |||
| d05aa6a5e0 | |||
| bc4ddbfb01 | |||
| 14499f5021 | |||
| aae43cbb77 | |||
| db6cb8dedc | |||
| 5092da074a | |||
| 1c6524db55 |
@@ -23,9 +23,6 @@ script:
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml $PHPUNIT_FLAGS
|
||||
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
|
||||
|
||||
after_script:
|
||||
- php vendor/bin/coveralls -v
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- php: hhvm
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Upgrade to 2.5
|
||||
|
||||
## Minor BC BREAK: query cache key time is now a float
|
||||
|
||||
As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
|
||||
instead of an integer in order to have more precision and also to be consistent
|
||||
with the `TimestampCacheEntry#time`.
|
||||
|
||||
## Minor BC BREAK: discriminator map must now include all non-transient classes
|
||||
|
||||
It is now required that you declare the root of an inheritance in the
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/yaml": "~2.3|~3.0",
|
||||
"phpunit/phpunit": "~4.0",
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
|
||||
@@ -28,7 +28,7 @@ use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
@@ -993,32 +993,54 @@ abstract class AbstractQuery
|
||||
private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
|
||||
{
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL);
|
||||
$queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
|
||||
$result = $queryCache->get($querykey, $rsm, $this->_hints);
|
||||
$queryKey = new QueryCacheKey(
|
||||
$this->getHash(),
|
||||
$this->lifetime,
|
||||
$this->cacheMode ?: Cache::MODE_NORMAL,
|
||||
$this->getTimestampKey()
|
||||
);
|
||||
|
||||
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
|
||||
|
||||
if ($result !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $querykey);
|
||||
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
|
||||
$cached = $queryCache->put($querykey, $rsm, $result, $this->_hints);
|
||||
$cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints);
|
||||
|
||||
if ($this->cacheLogger) {
|
||||
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $querykey);
|
||||
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
|
||||
|
||||
if ($cached) {
|
||||
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $querykey);
|
||||
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
|
||||
*/
|
||||
private function getTimestampKey()
|
||||
{
|
||||
$entityName = reset($this->_resultSetMapping->aliasMap);
|
||||
|
||||
if (empty($entityName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$metadata = $this->_em->getClassMetadata($entityName);
|
||||
|
||||
return new Cache\TimestampCacheKey($metadata->getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -110,7 +110,9 @@ class CacheConfiguration
|
||||
public function getQueryValidator()
|
||||
{
|
||||
if ($this->queryValidator === null) {
|
||||
$this->queryValidator = new TimestampQueryCacheValidator();
|
||||
$this->queryValidator = new TimestampQueryCacheValidator(
|
||||
$this->cacheFactory->getTimestampRegion()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->queryValidator;
|
||||
|
||||
@@ -73,7 +73,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity)
|
||||
{
|
||||
$data = $this->uow->getOriginalEntityData($entity);
|
||||
$data = array_merge($data, $key->identifier); // why update has no identifier values ?
|
||||
$data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ?
|
||||
|
||||
foreach ($metadata->associationMappings as $name => $assoc) {
|
||||
|
||||
|
||||
@@ -296,17 +296,16 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
* @param array $orderBy
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @param integer $timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null)
|
||||
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
{
|
||||
list($params) = ($criteria instanceof Criteria)
|
||||
? $this->persister->expandCriteriaParameters($criteria)
|
||||
: $this->persister->expandParameters($criteria);
|
||||
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp);
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,17 +376,16 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
//handle only EntityRepository#findOneBy
|
||||
$timestamp = $this->timestampRegion->get($this->timestampKey);
|
||||
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null);
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
|
||||
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
|
||||
$queryCache = $this->cache->getQueryCache($this->regionName);
|
||||
$result = $queryCache->get($querykey, $rsm);
|
||||
$result = $queryCache->get($queryKey, $rsm);
|
||||
|
||||
if ($result !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
return $result[0];
|
||||
@@ -397,15 +395,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return null;
|
||||
}
|
||||
|
||||
$cached = $queryCache->put($querykey, $rsm, array($result));
|
||||
$cached = $queryCache->put($queryKey, $rsm, array($result));
|
||||
|
||||
if ($this->cacheLogger) {
|
||||
if ($result) {
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
if ($cached) {
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,32 +415,31 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
|
||||
{
|
||||
$timestamp = $this->timestampRegion->get($this->timestampKey);
|
||||
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null);
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
|
||||
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
|
||||
$queryCache = $this->cache->getQueryCache($this->regionName);
|
||||
$result = $queryCache->get($querykey, $rsm);
|
||||
$result = $queryCache->get($queryKey, $rsm);
|
||||
|
||||
if ($result !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
|
||||
$cached = $queryCache->put($querykey, $rsm, $result);
|
||||
$cached = $queryCache->put($queryKey, $rsm, $result);
|
||||
|
||||
if ($this->cacheLogger) {
|
||||
if ($result) {
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
if ($cached) {
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,32 +513,34 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
public function loadCriteria(Criteria $criteria)
|
||||
{
|
||||
$orderBy = $criteria->getOrderings();
|
||||
$limit = $criteria->getMaxResults();
|
||||
$offset = $criteria->getFirstResult();
|
||||
$query = $this->persister->getSelectSQL($criteria);
|
||||
$timestamp = $this->timestampRegion->get($this->timestampKey);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
|
||||
$hash = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
|
||||
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
|
||||
$queryCache = $this->cache->getQueryCache($this->regionName);
|
||||
$cacheResult = $queryCache->get($querykey, $rsm);
|
||||
$cacheResult = $queryCache->get($queryKey, $rsm);
|
||||
|
||||
if ($cacheResult !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
return $cacheResult;
|
||||
}
|
||||
|
||||
$result = $this->persister->loadCriteria($criteria);
|
||||
$cached = $queryCache->put($querykey, $rsm, $result);
|
||||
$cached = $queryCache->put($queryKey, $rsm, $result);
|
||||
|
||||
if ($this->cacheLogger) {
|
||||
if ($result) {
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
|
||||
}
|
||||
|
||||
if ($cached) {
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
|
||||
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,18 +38,18 @@ class QueryCacheEntry implements CacheEntry
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @var integer Time creation of this cache entry
|
||||
* @var float Time creation of this cache entry
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* @param array $result
|
||||
* @param integer $time
|
||||
* @param float $time
|
||||
*/
|
||||
public function __construct($result, $time = null)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->time = $time ?: time();
|
||||
$this->time = $time ?: microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,14 +45,27 @@ class QueryCacheKey extends CacheKey
|
||||
public $cacheMode;
|
||||
|
||||
/**
|
||||
* @param string $hash Result cache id
|
||||
* @param integer $lifetime Query lifetime
|
||||
* @param integer $cacheMode Query cache mode
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @var TimestampCacheKey|null
|
||||
*/
|
||||
public function __construct($hash, $lifetime = 0, $cacheMode = Cache::MODE_NORMAL)
|
||||
{
|
||||
$this->hash = $hash;
|
||||
$this->lifetime = $lifetime;
|
||||
$this->cacheMode = $cacheMode;
|
||||
public $timestampKey;
|
||||
|
||||
/**
|
||||
* @param string $hash Result cache id
|
||||
* @param integer $lifetime Query lifetime
|
||||
* @param int $cacheMode Query cache mode
|
||||
* @param TimestampCacheKey|null $timestampKey
|
||||
*/
|
||||
public function __construct(
|
||||
$hash,
|
||||
$lifetime = 0,
|
||||
$cacheMode = Cache::MODE_NORMAL,
|
||||
TimestampCacheKey $timestampKey = null
|
||||
) {
|
||||
$this->hash = $hash;
|
||||
$this->lifetime = $lifetime;
|
||||
$this->cacheMode = $cacheMode;
|
||||
$this->timestampKey = $timestampKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,15 +26,49 @@ namespace Doctrine\ORM\Cache;
|
||||
*/
|
||||
class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
{
|
||||
/**
|
||||
* @var TimestampRegion
|
||||
*/
|
||||
private $timestampRegion;
|
||||
|
||||
/**
|
||||
* @param TimestampRegion $timestampRegion
|
||||
*/
|
||||
public function __construct(TimestampRegion $timestampRegion)
|
||||
{
|
||||
$this->timestampRegion = $timestampRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid(QueryCacheKey $key, QueryCacheEntry $entry)
|
||||
{
|
||||
if ($this->regionUpdated($key, $entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($key->lifetime == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($entry->time + $key->lifetime) > time();
|
||||
return ($entry->time + $key->lifetime) > microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryCacheKey $key
|
||||
* @param QueryCacheEntry $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry)
|
||||
{
|
||||
if ($key->timestampKey === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$timestamp = $this->timestampRegion->get($key->timestampKey);
|
||||
|
||||
return $timestamp && $timestamp->time > $entry->time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +332,9 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// Split the row data into chunks of class data.
|
||||
$rowData = $this->gatherRowData($row, $id, $nonemptyComponents);
|
||||
|
||||
// reset result pointers for each data row
|
||||
$this->resultPointers = [];
|
||||
|
||||
// Hydrate the data chunks
|
||||
foreach ($rowData['data'] as $dqlAlias => $data) {
|
||||
$entityName = $this->_rsm->aliasMap[$dqlAlias];
|
||||
|
||||
@@ -122,6 +122,9 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if value is null before conversion (because some types convert null to something else)
|
||||
$valueIsNull = null === $value;
|
||||
|
||||
// Convert field to a valid PHP value
|
||||
if (isset($cacheKeyInfo['type'])) {
|
||||
$type = $cacheKeyInfo['type'];
|
||||
@@ -131,7 +134,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
$fieldName = $cacheKeyInfo['fieldName'];
|
||||
|
||||
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
|
||||
if ( ! isset($data[$fieldName]) || $value !== null) {
|
||||
if ( ! isset($data[$fieldName]) || ! $valueIsNull) {
|
||||
$data[$fieldName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,8 +940,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
|
||||
$this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
|
||||
$fieldRefl = $reflService->getAccessibleProperty(
|
||||
isset($embeddedClass['declared']) ? $embeddedClass['declared'] : $this->name,
|
||||
$property
|
||||
);
|
||||
|
||||
$parentReflFields[$property] = $fieldRefl;
|
||||
$this->reflFields[$property] = $fieldRefl;
|
||||
}
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
|
||||
@@ -228,7 +228,7 @@ class ProxyFactory extends AbstractProxyFactory
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($class->getReflectionClass()->getProperties() as $property) {
|
||||
foreach ($class->getReflectionProperties() as $property) {
|
||||
if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2264,7 +2264,9 @@ class SqlWalker implements TreeWalker
|
||||
public function walkArithmeticFactor($factor)
|
||||
{
|
||||
if (is_string($factor)) {
|
||||
return $factor;
|
||||
return (isset($this->queryComponents[$factor]))
|
||||
? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
|
||||
: $factor;
|
||||
}
|
||||
|
||||
// Phase 2 AST optimization: Skip processing of ArithmeticFactor
|
||||
|
||||
@@ -117,7 +117,7 @@ class PhpExporter extends AbstractExporter
|
||||
$oneToOneMappingArray = array(
|
||||
'mappedBy' => $associationMapping['mappedBy'],
|
||||
'inversedBy' => $associationMapping['inversedBy'],
|
||||
'joinColumns' => $associationMapping['joinColumns'],
|
||||
'joinColumns' => $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : [],
|
||||
'orphanRemoval' => $associationMapping['orphanRemoval'],
|
||||
);
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ class YamlExporter extends AbstractExporter
|
||||
}
|
||||
|
||||
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
$joinColumns = $associationMapping['joinColumns'];
|
||||
$joinColumns = $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : [];
|
||||
$newJoinColumns = array();
|
||||
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
|
||||
@@ -2407,17 +2407,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$this->commitOrderCalculator->clear();
|
||||
}
|
||||
} else {
|
||||
$visited = array();
|
||||
|
||||
foreach ($this->identityMap as $className => $entities) {
|
||||
if ($className !== $entityName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$this->doDetach($entity, $visited, false);
|
||||
}
|
||||
}
|
||||
$this->clearIdentityMapForEntityName($entityName);
|
||||
$this->clearEntityInsertionsForEntityName($entityName);
|
||||
}
|
||||
|
||||
if ($this->evm->hasListeners(Events::onClear)) {
|
||||
@@ -3431,8 +3422,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
);
|
||||
$managedCol->setOwner($managedCopy, $assoc2);
|
||||
$prop->setValue($managedCopy, $managedCol);
|
||||
|
||||
$this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
|
||||
}
|
||||
|
||||
if ($assoc2['isCascadeMerge']) {
|
||||
@@ -3471,4 +3460,32 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
$this->hydrationCompleteHandler->hydrationComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName
|
||||
*/
|
||||
private function clearIdentityMapForEntityName($entityName)
|
||||
{
|
||||
if (! isset($this->identityMap[$entityName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$visited = [];
|
||||
|
||||
foreach ($this->identityMap[$entityName] as $entity) {
|
||||
$this->doDetach($entity, $visited, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName
|
||||
*/
|
||||
private function clearEntityInsertionsForEntityName($entityName)
|
||||
{
|
||||
foreach ($this->entityInsertions as $hash => $entity) {
|
||||
if (get_class($entity) === $entityName) {
|
||||
unset($this->entityInsertions[$hash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.5.3';
|
||||
const VERSION = '2.5.5-DEV';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Doctrine\Tests\Models\GeoNames;
|
||||
*/
|
||||
class Country
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="string", length=2)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Issue5989;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="issue5989_employees")
|
||||
*/
|
||||
class Issue5989Employee extends Issue5989Person
|
||||
{
|
||||
/**
|
||||
* @Column(type="simple_array", nullable=true)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tags;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Issue5989;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="issue5989_managers")
|
||||
*/
|
||||
class Issue5989Manager extends Issue5989Person
|
||||
{
|
||||
/**
|
||||
* @Column(type="simple_array", nullable=true)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tags;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Issue5989;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="issue5989_persons")
|
||||
* @InheritanceType("JOINED")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({
|
||||
* "person" = "Issue5989Person",
|
||||
* "manager" = "Issue5989Manager",
|
||||
* "employee" = "Issue5989Employee"
|
||||
* })
|
||||
*/
|
||||
class Issue5989Person
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Doctrine\Tests\ORM\Cache;
|
||||
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
use Doctrine\ORM\Cache\CacheConfiguration;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
@@ -64,6 +64,11 @@ class CacheConfigTest extends DoctrineTestCase
|
||||
|
||||
public function testSetGetQueryValidator()
|
||||
{
|
||||
$factory = $this->getMock('Doctrine\ORM\Cache\CacheFactory');
|
||||
$factory->method('getTimestampRegion')->willReturn($this->getMock('Doctrine\ORM\Cache\TimestampRegion'));
|
||||
|
||||
$this->config->setCacheFactory($factory);
|
||||
|
||||
$validator = $this->getMock('Doctrine\ORM\Cache\QueryCacheValidator');
|
||||
|
||||
$this->assertInstanceOf('Doctrine\ORM\Cache\TimestampQueryCacheValidator', $this->config->getQueryValidator());
|
||||
@@ -72,4 +77,4 @@ class CacheConfigTest extends DoctrineTestCase
|
||||
|
||||
$this->assertEquals($validator, $this->config->getQueryValidator());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class DefaultEntityHydratorTest extends OrmTestCase
|
||||
$this->assertArrayHasKey('name', $cache->data);
|
||||
$this->assertArrayHasKey('country', $cache->data);
|
||||
$this->assertEquals(array(
|
||||
'id' => 11,
|
||||
'id' => 12,
|
||||
'name' => 'Bar',
|
||||
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
|
||||
), $cache->data);
|
||||
@@ -147,9 +147,39 @@ class DefaultEntityHydratorTest extends OrmTestCase
|
||||
$this->assertArrayHasKey('name', $cache->data);
|
||||
$this->assertArrayHasKey('country', $cache->data);
|
||||
$this->assertEquals(array(
|
||||
'id' => 11,
|
||||
'id' => 12,
|
||||
'name' => 'Bar',
|
||||
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
|
||||
), $cache->data);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCacheEntryWithWrongIdentifierType()
|
||||
{
|
||||
$proxy = $this->em->getReference(Country::CLASSNAME, 11);
|
||||
$entity = new State('Bat', $proxy);
|
||||
$uow = $this->em->getUnitOfWork();
|
||||
$entityData = array('id'=> 12, 'name'=>'Bar', 'country' => $proxy);
|
||||
$metadata = $this->em->getClassMetadata(State::CLASSNAME);
|
||||
$key = new EntityCacheKey($metadata->name, array('id'=>'12'));
|
||||
|
||||
$entity->setId(12);
|
||||
|
||||
$uow->registerManaged($entity, array('id'=>12), $entityData);
|
||||
|
||||
$cache = $this->structure->buildCacheEntry($metadata, $key, $entity);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\ORM\Cache\CacheEntry', $cache);
|
||||
$this->assertInstanceOf('Doctrine\ORM\Cache\EntityCacheEntry', $cache);
|
||||
|
||||
$this->assertArrayHasKey('id', $cache->data);
|
||||
$this->assertArrayHasKey('name', $cache->data);
|
||||
$this->assertArrayHasKey('country', $cache->data);
|
||||
$this->assertSame($entity->getId(), $cache->data['id']);
|
||||
$this->assertEquals(array(
|
||||
'id' => 12,
|
||||
'name' => 'Bar',
|
||||
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
|
||||
), $cache->data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ class DefaultQueryCacheTest extends OrmTestCase
|
||||
array('id'=>2, 'name' => 'Bar')
|
||||
);
|
||||
|
||||
$entry->time = time() - 100;
|
||||
$entry->time = microtime(true) - 100;
|
||||
|
||||
$this->region->addReturn('get', $entry);
|
||||
$this->region->addReturn('get', new EntityCacheEntry(Country::CLASSNAME, $entities[0]));
|
||||
|
||||
@@ -1035,4 +1035,37 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
public function testQueryCacheShouldBeEvictedOnTimestampUpdate()
|
||||
{
|
||||
$this->loadFixturesCountries();
|
||||
$this->_em->clear();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$dql = 'SELECT country FROM Doctrine\Tests\Models\Cache\Country country';
|
||||
|
||||
$result1 = $this->_em->createQuery($dql)
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
$this->assertCount(2, $result1);
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$this->_em->persist(new Country('France'));
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$result2 = $this->_em->createQuery($dql)
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
$this->assertCount(3, $result2);
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
foreach ($result2 as $entity) {
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class DDC3303Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->_schemaTool->createSchema([$this->_em->getClassMetadata(DDC3303Employee::CLASSNAME)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 4097
|
||||
* @group 4277
|
||||
* @group 5867
|
||||
*
|
||||
* When using an embedded field in an inheritance, private properties should also be inherited.
|
||||
*/
|
||||
public function testEmbeddedObjectsAreAlsoInherited()
|
||||
{
|
||||
$employee = new DDC3303Employee(
|
||||
'John Doe',
|
||||
new DDC3303Address('Somewhere', 123, 'Over the rainbow'),
|
||||
'Doctrine Inc'
|
||||
);
|
||||
|
||||
$this->_em->persist($employee);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
self::assertEquals($employee, $this->_em->find(DDC3303Employee::CLASSNAME, 'John Doe'));
|
||||
}
|
||||
}
|
||||
|
||||
/** @MappedSuperclass */
|
||||
abstract class DDC3303Person
|
||||
{
|
||||
/** @Id @GeneratedValue(strategy="NONE") @Column(type="string") @var string */
|
||||
private $name;
|
||||
|
||||
/** @Embedded(class="DDC3303Address") @var DDC3303Address */
|
||||
private $address;
|
||||
|
||||
public function __construct($name, DDC3303Address $address)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->address = $address;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Embeddable
|
||||
*/
|
||||
class DDC3303Address
|
||||
{
|
||||
/** @Column(type="string") @var string */
|
||||
private $street;
|
||||
|
||||
/** @Column(type="integer") @var int */
|
||||
private $number;
|
||||
|
||||
/** @Column(type="string") @var string */
|
||||
private $city;
|
||||
|
||||
public function __construct($street, $number, $city)
|
||||
{
|
||||
$this->street = $street;
|
||||
$this->number = $number;
|
||||
$this->city = $city;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ddc3303_employee")
|
||||
*/
|
||||
class DDC3303Employee extends DDC3303Person
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/** @Column(type="string") @var string */
|
||||
private $company;
|
||||
|
||||
public function __construct($name, DDC3303Address $address, $company)
|
||||
{
|
||||
parent::__construct($name, $address);
|
||||
|
||||
$this->company = $company;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\Models\Cache\Country;
|
||||
use Doctrine\Tests\ORM\Functional\SecondLevelCacheAbstractTest;
|
||||
|
||||
class DDC3967Test extends SecondLevelCacheAbstractTest
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->loadFixturesCountries();
|
||||
$this->_em->getCache()->evictEntityRegion(Country::CLASSNAME);
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testIdentifierCachedWithProperType()
|
||||
{
|
||||
$country = array_pop($this->countries);
|
||||
$id = $country->getId();
|
||||
|
||||
// First time, loaded from database
|
||||
$this->_em->find(Country::CLASSNAME, "$id");
|
||||
$this->_em->clear();
|
||||
|
||||
// Second time, loaded from cache
|
||||
/** @var Country $country */
|
||||
$country = $this->_em->find(Country::CLASSNAME, "$id");
|
||||
|
||||
// Identifier type should be integer
|
||||
$this->assertSame($country->getId(), $id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* @group GH-5762
|
||||
*/
|
||||
class GH5762Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(GH5762Driver::CLASSNAME),
|
||||
$this->_em->getClassMetadata(GH5762DriverRide::CLASSNAME),
|
||||
$this->_em->getClassMetadata(GH5762Car::CLASSNAME),
|
||||
));
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$result = $this->fetchData();
|
||||
|
||||
self::assertInstanceOf(GH5762Driver::CLASSNAME, $result);
|
||||
self::assertInstanceOf('Doctrine\ORM\PersistentCollection', $result->driverRides);
|
||||
self::assertInstanceOf(GH5762DriverRide::CLASSNAME, $result->driverRides->get(0));
|
||||
self::assertInstanceOf(GH5762Car::CLASSNAME, $result->driverRides->get(0)->car);
|
||||
|
||||
$cars = array();
|
||||
foreach ($result->driverRides as $ride) {
|
||||
$cars[] = $ride->car->brand;
|
||||
}
|
||||
|
||||
self::assertEquals(count($cars), count(array_unique($cars)));
|
||||
|
||||
self::assertContains('BMW', $cars);
|
||||
self::assertContains('Crysler', $cars);
|
||||
self::assertContains('Dodge', $cars);
|
||||
self::assertContains('Mercedes', $cars);
|
||||
self::assertContains('Volvo', $cars);
|
||||
}
|
||||
|
||||
private function fetchData()
|
||||
{
|
||||
$this->createData();
|
||||
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('d, dr, c')
|
||||
->from(GH5762Driver::CLASSNAME, 'd')
|
||||
->leftJoin('d.driverRides', 'dr')
|
||||
->leftJoin('dr.car', 'c')
|
||||
->where('d.id = 1');
|
||||
|
||||
return $qb->getQuery()->getSingleResult();
|
||||
}
|
||||
|
||||
private function createData()
|
||||
{
|
||||
$car1 = new GH5762Car('BMW', '7 Series');
|
||||
$car2 = new GH5762Car('Crysler', '300');
|
||||
$car3 = new GH5762Car('Dodge', 'Dart');
|
||||
$car4 = new GH5762Car('Mercedes', 'C-Class');
|
||||
$car5 = new GH5762Car('Volvo', 'XC90');
|
||||
|
||||
$driver = new GH5762Driver(1, 'John Doe');
|
||||
|
||||
$ride1 = new GH5762DriverRide($driver, $car1);
|
||||
$ride2 = new GH5762DriverRide($driver, $car2);
|
||||
$ride3 = new GH5762DriverRide($driver, $car3);
|
||||
$ride4 = new GH5762DriverRide($driver, $car4);
|
||||
$ride5 = new GH5762DriverRide($driver, $car5);
|
||||
|
||||
$this->_em->persist($car1);
|
||||
$this->_em->persist($car2);
|
||||
$this->_em->persist($car3);
|
||||
$this->_em->persist($car4);
|
||||
$this->_em->persist($car5);
|
||||
|
||||
$this->_em->persist($driver);
|
||||
|
||||
$this->_em->persist($ride1);
|
||||
$this->_em->persist($ride2);
|
||||
$this->_em->persist($ride3);
|
||||
$this->_em->persist($ride4);
|
||||
$this->_em->persist($ride5);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="driver")
|
||||
*/
|
||||
class GH5762Driver
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="NONE")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=255);
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="GH5762DriverRide", mappedBy="driver")
|
||||
*/
|
||||
public $driverRides;
|
||||
|
||||
public function __construct($id, $name)
|
||||
{
|
||||
$this->driverRides = new ArrayCollection();
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="driver_ride")
|
||||
*/
|
||||
class GH5762DriverRide
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="GH5762Driver", inversedBy="driverRides")
|
||||
* @JoinColumn(name="driver_id", referencedColumnName="id")
|
||||
*/
|
||||
public $driver;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="GH5762Car", inversedBy="carRides")
|
||||
* @JoinColumn(name="car", referencedColumnName="brand")
|
||||
*/
|
||||
public $car;
|
||||
|
||||
function __construct(GH5762Driver $driver, GH5762Car $car)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->car = $car;
|
||||
|
||||
$this->driver->driverRides->add($this);
|
||||
$this->car->carRides->add($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="car")
|
||||
*/
|
||||
class GH5762Car
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="string", length=25)
|
||||
* @GeneratedValue(strategy="NONE")
|
||||
*/
|
||||
public $brand;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=255);
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="GH5762DriverRide", mappedBy="car")
|
||||
*/
|
||||
public $carRides;
|
||||
|
||||
public function __construct($brand, $model)
|
||||
{
|
||||
$this->carRides = new ArrayCollection();
|
||||
$this->brand = $brand;
|
||||
$this->model = $model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\Models\Issue5989\Issue5989Employee;
|
||||
use Doctrine\Tests\Models\Issue5989\Issue5989Manager;
|
||||
use Doctrine\Tests\Models\Issue5989\Issue5989Person;
|
||||
|
||||
/**
|
||||
* @group issue-5989
|
||||
*/
|
||||
class Issue5989Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->useModelSet('issue5989');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testSimpleArrayTypeHydratedCorrectlyInJoinedInheritance()
|
||||
{
|
||||
$manager = new Issue5989Manager();
|
||||
|
||||
$managerTags = ['tag1', 'tag2'];
|
||||
$manager->tags = $managerTags;
|
||||
$this->_em->persist($manager);
|
||||
|
||||
$employee = new Issue5989Employee();
|
||||
|
||||
$employeeTags =['tag2', 'tag3'];
|
||||
$employee->tags = $employeeTags;
|
||||
$this->_em->persist($employee);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$managerId = $manager->id;
|
||||
$employeeId = $employee->id;
|
||||
|
||||
// clear entity manager so that $repository->find actually fetches them and uses the hydrator
|
||||
// instead of just returning the existing managed entities
|
||||
$this->_em->clear();
|
||||
|
||||
$repository = $this->_em->getRepository(Issue5989Person::CLASSNAME);
|
||||
|
||||
$manager = $repository->find($managerId);
|
||||
$employee = $repository->find($employeeId);
|
||||
|
||||
static::assertEquals($managerTags, $manager->tags);
|
||||
static::assertEquals($employeeTags, $employee->tags);
|
||||
}
|
||||
}
|
||||
@@ -86,4 +86,34 @@ class SimpleObjectHydratorTest extends HydrationTestCase
|
||||
$hydrator = new \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator($this->_em);
|
||||
$hydrator->hydrateAll($stmt, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group issue-5989
|
||||
*/
|
||||
public function testNullValueShouldNotOverwriteFieldWithSameNameInJoinedInheritance()
|
||||
{
|
||||
$rsm = new ResultSetMapping;
|
||||
$rsm->addEntityResult('Doctrine\Tests\Models\Issue5989\Issue5989Person', 'p');
|
||||
$rsm->addFieldResult('p', 'p__id', 'id');
|
||||
$rsm->addFieldResult('p', 'm__tags', 'tags', 'Doctrine\Tests\Models\Issue5989\Issue5989Manager');
|
||||
$rsm->addFieldResult('p', 'e__tags', 'tags', 'Doctrine\Tests\Models\Issue5989\Issue5989Employee');
|
||||
$rsm->addMetaResult('p', 'discr', 'discr', false, 'string');
|
||||
$resultSet = array(
|
||||
array(
|
||||
'p__id' => '1',
|
||||
'm__tags' => 'tag1,tag2',
|
||||
'e__tags' => null,
|
||||
'discr' => 'manager'
|
||||
),
|
||||
);
|
||||
|
||||
$expectedEntity = new \Doctrine\Tests\Models\Issue5989\Issue5989Manager();
|
||||
$expectedEntity->id = 1;
|
||||
$expectedEntity->tags = ['tag1', 'tag2'];
|
||||
|
||||
$stmt = new HydratorMockStatement($resultSet);
|
||||
$hydrator = new \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator($this->_em);
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
$this->assertEquals($result[0], $expectedEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
|
||||
{
|
||||
$cm = new ClassMetadata('stdClass');
|
||||
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache());
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$annotationDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
|
||||
namespace Doctrine\Tests\ORM\Proxy;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService;
|
||||
use Doctrine\Common\Proxy\AbstractProxyFactory;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\Common\Proxy\ProxyGenerator;
|
||||
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||
use Doctrine\Tests\Mocks\DriverMock;
|
||||
use Doctrine\Tests\Mocks\EntityManagerMock;
|
||||
use Doctrine\Tests\Mocks\UnitOfWorkMock;
|
||||
use Doctrine\Tests\Mocks\DriverMock;
|
||||
use Doctrine\Common\Proxy\AbstractProxyFactory;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use Doctrine\Tests\Models\Company\CompanyEmployee;
|
||||
|
||||
/**
|
||||
* Test the proxy generator. Its work is generating on-the-fly subclasses of a given model, which implement the Proxy pattern.
|
||||
@@ -62,9 +64,9 @@ class ProxyFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
$persister
|
||||
->expects($this->atLeastOnce())
|
||||
->method('load')
|
||||
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
|
||||
->will($this->returnValue(new \stdClass()));
|
||||
->method('load')
|
||||
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
|
||||
->will($this->returnValue(new \stdClass()));
|
||||
|
||||
$proxy->getDescription();
|
||||
}
|
||||
@@ -75,7 +77,7 @@ class ProxyFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testSkipAbstractClassesOnGeneration()
|
||||
{
|
||||
$cm = new ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
|
||||
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
$cm->initializeReflection(new RuntimeReflectionService);
|
||||
$this->assertNotNull($cm->reflClass);
|
||||
|
||||
$num = $this->proxyFactory->generateProxyClasses(array($cm));
|
||||
@@ -136,6 +138,45 @@ class ProxyFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertInstanceOf('Closure', $proxy->__getInitializer(), 'The initializer wasn\'t removed');
|
||||
$this->assertInstanceOf('Closure', $proxy->__getCloner(), 'The cloner wasn\'t removed');
|
||||
}
|
||||
|
||||
public function testProxyClonesParentFields()
|
||||
{
|
||||
$companyEmployee = new CompanyEmployee();
|
||||
$companyEmployee->setSalary(1000); // A property on the CompanyEmployee
|
||||
$companyEmployee->setName("Bob"); // A property on the parent class, CompanyPerson
|
||||
|
||||
// Set the id of the CompanyEmployee (which is in the parent CompanyPerson)
|
||||
$class = new \ReflectionClass('Doctrine\Tests\Models\Company\CompanyPerson');
|
||||
|
||||
$property = $class->getProperty('id');
|
||||
$property->setAccessible(true);
|
||||
|
||||
$property->setValue($companyEmployee, 42);
|
||||
|
||||
$classMetaData = $this->emMock->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee');
|
||||
|
||||
$persister = $this->getMock('Doctrine\ORM\Persisters\Entity\BasicEntityPersister', array('load', 'getClassMetadata'), array(), '', false);
|
||||
$this->uowMock->setEntityPersister('Doctrine\Tests\Models\Company\CompanyEmployee', $persister);
|
||||
|
||||
/* @var $proxy \Doctrine\Common\Proxy\Proxy */
|
||||
$proxy = $this->proxyFactory->getProxy('Doctrine\Tests\Models\Company\CompanyEmployee', array('id' => 42));
|
||||
|
||||
$persister
|
||||
->expects($this->atLeastOnce())
|
||||
->method('load')
|
||||
->will($this->returnValue($companyEmployee));
|
||||
|
||||
$persister
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnValue($classMetaData));
|
||||
|
||||
$cloned = clone $proxy;
|
||||
|
||||
$this->assertEquals(42, $cloned->getId(), "Expected the Id to be cloned");
|
||||
$this->assertEquals(1000, $cloned->getSalary(), "Expect properties on the CompanyEmployee class to be cloned");
|
||||
$this->assertEquals("Bob", $cloned->getName(), "Expect properties on the CompanyPerson class to be cloned");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractClass
|
||||
|
||||
@@ -2242,6 +2242,27 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING sclr_0 IS NULL'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* GitHub issue #4764: https://github.com/doctrine/doctrine2/issues/4764
|
||||
* @group DDC-3907
|
||||
* @dataProvider mathematicOperatorsProvider
|
||||
*/
|
||||
public function testHavingRegressionUsingVariableWithMathOperatorsExpression($operator)
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT COUNT(u.name) AS countName FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING 1 ' . $operator . ' countName > 0',
|
||||
'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING 1 ' . $operator . ' sclr_0 > 0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function mathematicOperatorsProvider()
|
||||
{
|
||||
return [['+'], ['-'], ['*'], ['/']];
|
||||
}
|
||||
}
|
||||
|
||||
class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
|
||||
|
||||
@@ -57,6 +57,18 @@ $metadata->mapOneToOne(array(
|
||||
'orphanRemoval' => true,
|
||||
'fetch' => ClassMetadataInfo::FETCH_EAGER,
|
||||
));
|
||||
$metadata->mapOneToOne(array(
|
||||
'fieldName' => 'cart',
|
||||
'targetEntity' => 'Doctrine\\Tests\\ORM\\Tools\\Export\\Cart',
|
||||
'mappedBy' => 'user',
|
||||
'cascade' =>
|
||||
array(
|
||||
0 => 'persist',
|
||||
),
|
||||
'inversedBy' => NULL,
|
||||
'orphanRemoval' => false,
|
||||
'fetch' => ClassMetadataInfo::FETCH_EAGER,
|
||||
));
|
||||
$metadata->mapOneToMany(array(
|
||||
'fieldName' => 'phonenumbers',
|
||||
'targetEntity' => 'Doctrine\\Tests\\ORM\\Tools\\Export\\Phonenumber',
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
<join-column name="address_id" referenced-column-name="id" on-delete="CASCADE" on-update="CASCADE"/>
|
||||
</one-to-one>
|
||||
|
||||
<one-to-one field="cart" target-entity="Doctrine\Tests\ORM\Tools\Export\Cart" mapped-by="user">
|
||||
<cascade>
|
||||
<cascade-remove/>
|
||||
</cascade>
|
||||
</one-to-one>
|
||||
|
||||
<many-to-one field="mainGroup" target-entity="Doctrine\Tests\ORM\Tools\Export\Group" />
|
||||
|
||||
<one-to-many field="phonenumbers" target-entity="Doctrine\Tests\ORM\Tools\Export\Phonenumber" mapped-by="user" orphan-removal="true" fetch="LAZY">
|
||||
|
||||
+4
@@ -30,6 +30,10 @@ Doctrine\Tests\ORM\Tools\Export\User:
|
||||
inversedBy: user
|
||||
orphanRemoval: true
|
||||
fetch: EAGER
|
||||
cart:
|
||||
targetEntity: Doctrine\Tests\ORM\Tools\Export\Cart
|
||||
mappedBy: user
|
||||
cascade: [ remove ]
|
||||
manyToOne:
|
||||
mainGroup:
|
||||
targetEntity: Doctrine\Tests\ORM\Tools\Export\Group
|
||||
|
||||
@@ -12,8 +12,13 @@ use Doctrine\Tests\Mocks\DriverMock;
|
||||
use Doctrine\Tests\Mocks\EntityManagerMock;
|
||||
use Doctrine\Tests\Mocks\EntityPersisterMock;
|
||||
use Doctrine\Tests\Mocks\UnitOfWorkMock;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\Forum\ForumAvatar;
|
||||
use Doctrine\Tests\Models\Forum\ForumUser;
|
||||
use Doctrine\Tests\Models\GeoNames\City;
|
||||
use Doctrine\Tests\Models\GeoNames\Country;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
@@ -321,6 +326,28 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertTrue($this->_unitOfWork->isInIdentityMap($entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 5849
|
||||
* @group 5850
|
||||
*/
|
||||
public function testPersistedEntityAndClearManager()
|
||||
{
|
||||
$entity1 = new City(123, 'London');
|
||||
$entity2 = new Country(456, 'United Kingdom');
|
||||
|
||||
$this->_unitOfWork->persist($entity1);
|
||||
$this->assertTrue($this->_unitOfWork->isInIdentityMap($entity1));
|
||||
|
||||
$this->_unitOfWork->persist($entity2);
|
||||
$this->assertTrue($this->_unitOfWork->isInIdentityMap($entity2));
|
||||
|
||||
$this->_unitOfWork->clear(Country::CLASSNAME);
|
||||
$this->assertTrue($this->_unitOfWork->isInIdentityMap($entity1));
|
||||
$this->assertFalse($this->_unitOfWork->isInIdentityMap($entity2));
|
||||
$this->assertTrue($this->_unitOfWork->isScheduledForInsert($entity1));
|
||||
$this->assertFalse($this->_unitOfWork->isScheduledForInsert($entity2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Provider
|
||||
*
|
||||
@@ -337,6 +364,34 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
|
||||
[new ArrayCollection()],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 5689
|
||||
* @group 1465
|
||||
*/
|
||||
public function testObjectHashesOfMergedEntitiesAreNotUsedInOriginalEntityDataMap()
|
||||
{
|
||||
$user = new CmsUser();
|
||||
$user->name = 'ocramius';
|
||||
$mergedUser = $this->_unitOfWork->merge($user);
|
||||
|
||||
self::assertSame([], $this->_unitOfWork->getOriginalEntityData($user), 'No original data was stored');
|
||||
self::assertSame([], $this->_unitOfWork->getOriginalEntityData($mergedUser), 'No original data was stored');
|
||||
|
||||
|
||||
$user = null;
|
||||
$mergedUser = null;
|
||||
|
||||
// force garbage collection of $user (frees the used object hashes, which may be recycled)
|
||||
gc_collect_cycles();
|
||||
|
||||
$newUser = new CmsUser();
|
||||
$newUser->name = 'ocramius';
|
||||
|
||||
$this->_unitOfWork->persist($newUser);
|
||||
|
||||
self::assertSame([], $this->_unitOfWork->getOriginalEntityData($newUser), 'No original data was stored');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -284,6 +284,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
'Doctrine\Tests\Models\VersionedManyToOne\Category',
|
||||
'Doctrine\Tests\Models\VersionedManyToOne\Article',
|
||||
),
|
||||
'issue5989' => array(
|
||||
'Doctrine\Tests\Models\Issue5989\Issue5989Person',
|
||||
'Doctrine\Tests\Models\Issue5989\Issue5989Employee',
|
||||
'Doctrine\Tests\Models\Issue5989\Issue5989Manager',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -544,6 +549,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
$conn->executeUpdate('DELETE FROM versioned_many_to_one_category');
|
||||
}
|
||||
|
||||
if (isset($this->_usedModelSets['issue5989'])) {
|
||||
$conn->executeUpdate('DELETE FROM issue5989_persons');
|
||||
$conn->executeUpdate('DELETE FROM issue5989_employees');
|
||||
$conn->executeUpdate('DELETE FROM issue5989_managers');
|
||||
}
|
||||
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user