1
0
mirror of synced 2025-01-19 06:51:40 +03:00

Merge branch 'master' of github.com:doctrine/doctrine2

This commit is contained in:
Benjamin Eberlei 2014-02-21 16:14:57 +01:00
commit f5897d4b0b
42 changed files with 115 additions and 125 deletions

View File

@ -20,7 +20,7 @@ before_script:
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi"
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi"
- composer install --prefer-dist --dev
- composer install --prefer-source --dev
script: phpunit -v --configuration tests/travis/$DB.travis.xml

View File

@ -6,22 +6,6 @@ design generally refer to best practices when working with Doctrine
and do not necessarily reflect best practices for database design
in general.
Don't use public properties on entities
---------------------------------------
It is very important that you don't map public properties on
entities, but only protected or private ones. The reason for this
is simple, whenever you access a public property of a proxy object
that hasn't been initialized yet the return value will be null.
Doctrine cannot hook into this process and magically make the
entity lazy load.
This can create situations where it is very hard to debug the
current failure. We therefore urge you to map only private and
protected properties on entities and use getter methods or magic
\_\_get() to access them.
Constrain relationships as much as possible
-------------------------------------------

View File

@ -100,6 +100,7 @@ of several common elements:
joinColumn:
name: address_id
referencedColumnName: id
onDelete: CASCADE
oneToMany:
phonenumbers:
targetEntity: Phonenumber

View File

@ -15,7 +15,9 @@ the first time its accessed. If you mark an association as extra lazy the follow
can be called without triggering a full load of the collection:
- ``Collection#contains($entity)``
- ``Collection#containsKey($key)`` (available with Doctrine 2.5)
- ``Collection#count()``
- ``Collection#get($key)`` (available with Doctrine 2.4)
- ``Collection#slice($offset, $length = null)``
For each of this three methods the following semantics apply:

View File

@ -29,7 +29,6 @@ use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\ORMInvalidArgumentException;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.

View File

@ -20,8 +20,6 @@
namespace Doctrine\ORM;
use Doctrine\ORM\EntityManagerInterface;
/**
* Provides an API for querying/managing the second level cache regions.
*

View File

@ -21,10 +21,7 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Cache\CacheFactory;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\QueryCacheValidator;
use Doctrine\ORM\Cache\TimestampQueryCacheValidator;
/**
* Configuration container for second-level cache.

View File

@ -22,8 +22,6 @@ namespace Doctrine\ORM\Cache;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\CollectionCacheEntry;
/**
* Hydrator cache entry for collections

View File

@ -20,8 +20,6 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Lock;
/**
* Defines contract for concurrently managed data region.
* It should be able to lock an specific cache entry in an atomic operation.

View File

@ -24,8 +24,6 @@ use Doctrine\Common\Cache\CacheProvider;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\TimestampRegion;
use Doctrine\ORM\Cache\RegionsConfiguration;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\Region\DefaultRegion;

View File

@ -24,8 +24,6 @@ use Doctrine\ORM\Query;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\CollectionCacheEntry;
/**
* Default hydrator cache for collections

View File

@ -23,10 +23,8 @@ namespace Doctrine\ORM\Cache;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Query;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\EntityCacheEntry;
/**
* Default hydrator cache for entities

View File

@ -25,10 +25,7 @@ use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache\QueryCacheEntry;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Cache\CacheException;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Query;

View File

@ -20,9 +20,7 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache\EntityCacheEntry;
/**
* Hydrator cache entry for entities

View File

@ -508,7 +508,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
if ($hasCache && ! empty($list)) {
if ($hasCache) {
$persister->storeCollectionCache($key, $list);
if ($this->cacheLogger) {
@ -543,7 +543,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
if ($hasCache && ! empty($list)) {
if ($hasCache) {
$persister->storeCollectionCache($key, $list);
if ($this->cacheLogger) {

View File

@ -20,8 +20,6 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\QueryCacheEntry;
/**
* Cache query validator interface.
*

View File

@ -20,8 +20,6 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Lock;
/**
* Defines a contract for accessing a particular named region.
*

View File

@ -20,9 +20,6 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\QueryCacheEntry;
use Doctrine\ORM\Cache\QueryCacheKey;
/**
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Event;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs;
/**

View File

@ -19,9 +19,6 @@
namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs;
/**

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\EntityManager;
/**

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**

View File

@ -2370,7 +2370,7 @@ class ClassMetadataInfo implements ClassMetadata
$queryMapping['isSelfClass'] = false;
if (isset($queryMapping['resultClass'])) {
if($queryMapping['resultClass'] === '__CLASS__') {
if ($queryMapping['resultClass'] === '__CLASS__') {
$queryMapping['isSelfClass'] = true;
$queryMapping['resultClass'] = $this->name;
@ -2410,7 +2410,7 @@ class ClassMetadataInfo implements ClassMetadata
}
$entityResult['isSelfClass'] = false;
if($entityResult['entityClass'] === '__CLASS__') {
if ($entityResult['entityClass'] === '__CLASS__') {
$entityResult['isSelfClass'] = true;
$entityResult['entityClass'] = $this->name;
@ -2430,7 +2430,7 @@ class ClassMetadataInfo implements ClassMetadata
if (!isset($field['column'])) {
$fieldName = $field['name'];
if(strpos($fieldName, '.')){
if (strpos($fieldName, '.')) {
list(, $fieldName) = explode('.', $fieldName);
}
@ -2583,7 +2583,7 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function addLifecycleCallback($callback, $event)
{
if(isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
return;
}

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**

View File

@ -25,7 +25,6 @@ use Doctrine\Common\Proxy\ProxyDefinition;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Proxy\Proxy as BaseProxy;
use Doctrine\Common\Proxy\ProxyGenerator;
use Doctrine\ORM\ORMInvalidArgumentException;
use Doctrine\ORM\Persisters\EntityPersister;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityNotFoundException;

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
/**

View File

@ -26,9 +26,6 @@ use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Collections\Expr\CompositeExpression;
use Doctrine\Common\Collections\Expr\Value;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Parameter;
/**
* Converts Collection expressions to Query expressions.
*

View File

@ -23,7 +23,6 @@ use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
@ -895,6 +894,8 @@ class SqlWalker implements TreeWalker
}
}
$targetTableJoin = null;
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
// The owning side is necessary at this point because only it contains the JoinColumn information.
@ -929,7 +930,10 @@ class SqlWalker implements TreeWalker
$conditions[] = $filterExpr;
}
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
$targetTableJoin = array(
'table' => $targetTableName . ' ' . $targetTableAlias,
'condition' => implode(' AND ', $conditions),
);
break;
case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
@ -981,20 +985,33 @@ class SqlWalker implements TreeWalker
$conditions[] = $filterExpr;
}
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
$targetTableJoin = array(
'table' => $targetTableName . ' ' . $targetTableAlias,
'condition' => implode(' AND ', $conditions),
);
break;
default:
throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');
}
// Handle WITH clause
if ($condExpr !== null) {
// Phase 2 AST optimization: Skip processing of ConditionalExpression
// if only one ConditionalTerm is defined
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
$withCondition = (null === $condExpr) ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')');
if ($targetClass->isInheritanceTypeJoined()) {
$ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
// If we have WITH condition, we need to build nested joins for target class table and cti joins
if ($withCondition) {
$sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
} else {
$sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;
}
} else {
$sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];
}
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
if ($targetClass->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
if ($withCondition) {
$sql .= ' AND ' . $withCondition;
}
// Apply the indexes

View File

@ -74,11 +74,11 @@ class GenerateEntitiesCommand extends Command
'Flag to define if generator should only update entity if it exists.', true
),
new InputOption(
'extend', null, InputOption::VALUE_OPTIONAL,
'extend', null, InputOption::VALUE_REQUIRED,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'num-spaces', null, InputOption::VALUE_REQUIRED,
'Defines the number of indentation spaces', 4
),
new InputOption(

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

View File

@ -19,7 +19,6 @@
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

View File

@ -19,8 +19,6 @@
namespace Doctrine\ORM\Tools\Export;
use Doctrine\ORM\Tools\Export\ExportException;
/**
* Class used for converting your mapping information between the
* supported formats: yaml, xml, and php/annotation.

View File

@ -16,7 +16,6 @@ namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
/**
* Wraps the query in order to select root entity IDs for pagination.
@ -100,9 +99,9 @@ class LimitSubqueryOutputWalker extends SqlWalker
$hiddens[$idx] = $expr->hiddenAliasResultVariable;
$expr->hiddenAliasResultVariable = false;
}
$innerSql = parent::walkSelectStatement($AST);
// Restore hiddens
foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
$expr->hiddenAliasResultVariable = $hiddens[$idx];
@ -164,11 +163,8 @@ class LimitSubqueryOutputWalker extends SqlWalker
$sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result',
implode(', ', $sqlIdentifier), $innerSql);
if ($this->platform instanceof PostgreSqlPlatform ||
$this->platform instanceof OraclePlatform) {
// http://www.doctrine-project.org/jira/browse/DDC-1958
$this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql);
}
// http://www.doctrine-project.org/jira/browse/DDC-1958
$sql = $this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql);
// Apply the limit and offset.
$sql = $this->platform->modifyLimitQuery(
@ -185,7 +181,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
return $sql;
}
/**
* Generates new SQL for Postgresql or Oracle if necessary.
*
@ -196,7 +192,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
*
* @return void
*/
public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql)
public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, $sql)
{
// For every order by, find out the SQL alias by inspecting the ResultSetMapping.
$sqlOrderColumns = array();
@ -219,11 +215,6 @@ class LimitSubqueryOutputWalker extends SqlWalker
$sqlOrderColumns = array_diff($sqlOrderColumns, $sqlIdentifier);
}
// We don't need orderBy in inner query.
// However at least on 5.4.6 I'm getting a segmentation fault and thus we don't clear it for now.
/*$AST->orderByClause = null;
$innerSql = parent::walkSelectStatement($AST);*/
if (count($orderBy)) {
$sql = sprintf(
'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s',
@ -232,5 +223,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
implode(', ', $orderBy)
);
}
return $sql;
}
}

View File

@ -20,7 +20,6 @@
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
@ -28,7 +27,6 @@ use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Internal\CommitOrderCalculator;
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;

View File

@ -236,20 +236,6 @@ class SchemaValidator
}
}
foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) {
if ($publicAttr->isStatic()) {
continue;
}
if ( ! isset($class->fieldMappings[$publicAttr->getName()]) &&
! isset($class->associationMappings[$publicAttr->getName()])) {
continue;
}
$ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ".
"or protected. Public fields may break lazy-loading.";
}
foreach ($class->subClasses as $subClass) {
if (!in_array($class->name, class_parents($subClass))) {
$ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".

View File

@ -38,12 +38,6 @@ class SchemaValidatorTest extends \Doctrine\Tests\OrmFunctionalTestCase
foreach ($classes as $class) {
$ce = $validator->validateClass($class);
foreach ($ce as $key => $error) {
if (strpos($error, "must be private or protected. Public fields may break lazy-loading.") !== false) {
unset($ce[$key]);
}
}
$this->assertEquals(0, count($ce), "Invalid Modelset: " . $modelSet . " class " . $class->name . ": ". implode("\n", $ce));
}
}

View File

@ -121,7 +121,7 @@ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase
$this->_em->flush();
}
protected function loadFixturesTravelersWithProfile()
{
$t1 = new Traveler("Test traveler 1");
@ -139,7 +139,7 @@ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase
$this->travelersWithProfile[] = $t1;
$this->travelersWithProfile[] = $t2;
$this->_em->flush();
}
@ -165,6 +165,7 @@ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase
{
$t1 = new Travel($this->travelers[0]);
$t2 = new Travel($this->travelers[1]);
$t3 = new Travel($this->travelers[1]);
$t1->addVisitedCity($this->cities[0]);
$t1->addVisitedCity($this->cities[1]);
@ -175,9 +176,11 @@ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase
$this->_em->persist($t1);
$this->_em->persist($t2);
$this->_em->persist($t3);
$this->travels[] = $t1;
$this->travels[] = $t2;
$this->travels[] = $t3;
$this->_em->flush();
}

View File

@ -171,7 +171,7 @@ class SecondLevelCacheManyToManyTest extends SecondLevelCacheAbstractTest
$this->_em->flush();
$this->_em->clear();
$this->assertTrue($this->cache->containsEntity(Traveler::CLASSNAME, $travel->getId()));
$this->assertTrue($this->cache->containsEntity(Travel::CLASSNAME, $travel->getId()));
$this->assertTrue($this->cache->containsEntity(Traveler::CLASSNAME, $traveler->getId()));
$this->assertTrue($this->cache->containsEntity(City::CLASSNAME, $this->cities[0]->getId()));
$this->assertTrue($this->cache->containsEntity(City::CLASSNAME, $this->cities[1]->getId()));
@ -214,4 +214,33 @@ class SecondLevelCacheManyToManyTest extends SecondLevelCacheAbstractTest
$this->_em->persist($travel);
$this->_em->flush();
}
public function testManyToManyWithEmptyRelation()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->loadFixturesTraveler();
$this->loadFixturesTravels();
$this->_em->clear();
$this->evictRegions();
$queryCount = $this->getCurrentQueryCount();
$entitiId = $this->travels[2]->getId(); //empty travel
$entity = $this->_em->find(Travel::CLASSNAME, $entitiId);
$this->assertEquals(0, $entity->getVisitedCities()->count());
$this->assertEquals($queryCount+2, $this->getCurrentQueryCount());
$this->_em->clear();
$entity = $this->_em->find(Travel::CLASSNAME, $entitiId);
$queryCount = $this->getCurrentQueryCount();
$this->assertEquals(0, $entity->getVisitedCities()->count());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
}
}

View File

@ -90,7 +90,7 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$s3 = $this->_em->find(State::CLASSNAME, $this->states[0]->getId());
$s4 = $this->_em->find(State::CLASSNAME, $this->states[1]->getId());
//trigger lazy load from cache
$this->assertCount(2, $s3->getCities());
$this->assertCount(2, $s4->getCities());
@ -133,12 +133,12 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
//trigger lazy load from database
$this->assertCount(2, $this->_em->find(State::CLASSNAME, $this->states[0]->getId())->getCities());
$this->assertTrue($this->cache->containsEntity(State::CLASSNAME, $this->states[0]->getId()));
$this->assertTrue($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[0]->getId()));
$this->assertTrue($this->cache->containsEntity(City::CLASSNAME, $this->states[0]->getCities()->get(0)->getId()));
$this->assertTrue($this->cache->containsEntity(City::CLASSNAME, $this->states[0]->getCities()->get(1)->getId()));
$queryCount = $this->getCurrentQueryCount();
$stateId = $this->states[0]->getId();
$state = $this->_em->find(State::CLASSNAME, $stateId);
@ -284,6 +284,35 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
}
public function testOneToManyWithEmptyRelation()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->secondLevelCacheLogger->clearStats();
$this->cache->evictEntityRegion(City::CLASSNAME);
$this->cache->evictEntityRegion(State::CLASSNAME);
$this->cache->evictCollectionRegion(State::CLASSNAME, 'cities');
$this->_em->clear();
$entitiId = $this->states[2]->getId(); // bavaria (cities count = 0)
$queryCount = $this->getCurrentQueryCount();
$entity = $this->_em->find(State::CLASSNAME, $entitiId);
$this->assertEquals(0, $entity->getCities()->count());
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount());
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$entity = $this->_em->find(State::CLASSNAME, $entitiId);
$this->assertEquals(0, $entity->getCities()->count());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
}
public function testOneToManyCount()
{
$this->loadFixturesCountries();

View File

@ -2065,7 +2065,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1',
'SELECT c0_.id AS id0 FROM company_organizations c1_ INNER JOIN company_events c0_ ON c1_.id = c0_.org_id AND (c0_.id = ?) LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id',
'SELECT c0_.id AS id0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}