diff --git a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php index 2d15c3c25..53603d4b0 100644 --- a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php +++ b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -267,10 +267,6 @@ class DefaultQueryCache implements QueryCache continue; } - if ( ! isset($assoc['cache'])) { - throw CacheException::nonCacheableEntityAssociation($entityName, $name); - } - $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); $assocRegion = $assocPersister->getCacheRegion(); $assocMetadata = $assocPersister->getClassMetadata(); diff --git a/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php b/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php index eaead7cf7..68e5c034d 100644 --- a/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php @@ -234,14 +234,6 @@ abstract class AbstractEntityPersister implements CachedEntityPersister $class = $this->metadataFactory->getMetadataFor($className); } - if ($class->containsForeignIdentifier) { - foreach ($class->associationMappings as $name => $assoc) { - if (!empty($assoc['id']) && !isset($assoc['cache'])) { - throw CacheException::nonCacheableEntityAssociation($class->name, $name); - } - } - } - $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); $cached = $this->region->put($key, $entry); diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 79dcdb2cb..a442c016f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -28,6 +28,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use ReflectionClass; use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\Common\ClassLoader; +use Doctrine\ORM\Cache\CacheException; /** * A ClassMetadata instance holds all the object-relational mapping metadata @@ -1078,6 +1079,17 @@ class ClassMetadataInfo implements ClassMetadata * @return void */ public function enableAssociationCache($fieldName, array $cache) + { + $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults ($fieldName, $cache); + } + + /** + * @param string $fieldName + * @param array $cache + * + * @return array + */ + public function getAssociationCacheDefaults($fieldName, array $cache) { if ( ! isset($cache['usage'])) { $cache['usage'] = isset($this->cache['usage']) @@ -1089,7 +1101,7 @@ class ClassMetadataInfo implements ClassMetadata $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; } - $this->associationMappings[$fieldName]['cache'] = $cache; + return $cache; } /** @@ -1475,6 +1487,10 @@ class ClassMetadataInfo implements ClassMetadata if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { $this->isIdentifierComposite = true; } + + if ($this->cache && !isset($mapping['cache'])) { + throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']); + } } // Mandatory attributes for both sides diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index df8b7b80c..e8af86e84 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -273,7 +273,13 @@ class AnnotationDriver extends AbstractAnnotationDriver $mapping = array(); $mapping['fieldName'] = $property->getName(); - + // Evaluate @Cache annotation + if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Cache')) !== null) { + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], array( + 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), + 'region' => $cacheAnnot->region, + )); + } // Check for JoinColumn/JoinColumns annotations $joinColumns = array(); @@ -396,14 +402,6 @@ class AnnotationDriver extends AbstractAnnotationDriver $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix; $metadata->mapEmbedded($mapping); } - - // Evaluate @Cache annotation - if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Cache')) !== null) { - $metadata->enableAssociationCache($mapping['fieldName'], array( - 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), - 'region' => $cacheAnnot->region, - )); - } } // Evaluate AssociationOverrides annotation diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index c917eb245..7f9a7ecc9 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -384,12 +384,12 @@ class XmlDriver extends FileDriver $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']); } - $metadata->mapOneToOne($mapping); - // Evaluate second level cache if (isset($oneToOneElement->cache)) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache)); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache)); } + + $metadata->mapOneToOne($mapping); } } @@ -428,12 +428,12 @@ class XmlDriver extends FileDriver throw new \InvalidArgumentException(" is not a valid tag"); } - $metadata->mapOneToMany($mapping); - // Evaluate second level cache if (isset($oneToManyElement->cache)) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache)); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache)); } + + $metadata->mapOneToMany($mapping); } } @@ -473,12 +473,13 @@ class XmlDriver extends FileDriver $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade); } - $metadata->mapManyToOne($mapping); - // Evaluate second level cache if (isset($manyToOneElement->cache)) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache)); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache)); } + + $metadata->mapManyToOne($mapping); + } } @@ -543,12 +544,12 @@ class XmlDriver extends FileDriver throw new \InvalidArgumentException(" is not a valid tag"); } - $metadata->mapManyToMany($mapping); - // Evaluate second level cache if (isset($manyToManyElement->cache)) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache)); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache)); } + + $metadata->mapManyToMany($mapping); } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 95d148318..aa288d0cd 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -388,12 +388,12 @@ class YamlDriver extends FileDriver $mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval']; } - $metadata->mapOneToOne($mapping); - // Evaluate second level cache if (isset($oneToOneElement['cache'])) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement['cache'])); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement['cache'])); } + + $metadata->mapOneToOne($mapping); } } @@ -426,12 +426,13 @@ class YamlDriver extends FileDriver $mapping['indexBy'] = $oneToManyElement['indexBy']; } - $metadata->mapOneToMany($mapping); // Evaluate second level cache if (isset($oneToManyElement['cache'])) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement['cache'])); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement['cache'])); } + + $metadata->mapOneToMany($mapping); } } @@ -475,12 +476,12 @@ class YamlDriver extends FileDriver $mapping['cascade'] = $manyToOneElement['cascade']; } - $metadata->mapManyToOne($mapping); - // Evaluate second level cache if (isset($manyToOneElement['cache'])) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement['cache'])); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement['cache'])); } + + $metadata->mapManyToOne($mapping); } } @@ -552,12 +553,12 @@ class YamlDriver extends FileDriver $mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval']; } - $metadata->mapManyToMany($mapping); - // Evaluate second level cache if (isset($manyToManyElement['cache'])) { - $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement['cache'])); + $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement['cache'])); } + + $metadata->mapManyToMany($mapping); } } diff --git a/tests/Doctrine/Tests/ORM/Cache/DefaultQueryCacheTest.php b/tests/Doctrine/Tests/ORM/Cache/DefaultQueryCacheTest.php index 5e6bbf8d7..86d040070 100644 --- a/tests/Doctrine/Tests/ORM/Cache/DefaultQueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Cache/DefaultQueryCacheTest.php @@ -482,30 +482,6 @@ class DefaultQueryCacheTest extends OrmTestCase $this->assertNull($this->queryCache->get($key, $rsm)); } - /** - * @expectedException Doctrine\ORM\Cache\CacheException - * @expectedExceptionMessage Entity association field "Doctrine\Tests\Models\Cache\City#travels" not configured as part of the second-level cache. - */ - public function testQueryNotCacheableAssociationException() - { - $uow = $this->em->getUnitOfWork(); - $key = new QueryCacheKey('query.key1', 0); - $rsm = new ResultSetMappingBuilder($this->em); - $cityClass = $this->em->getClassMetadata(City::CLASSNAME); - $city = new City("City 1", null); - $result = array( - $city - ); - - $cityClass->setFieldValue($city, 'id', 1); - - $rsm->addRootEntityFromClassMetadata(City::CLASSNAME, 'c'); - $rsm->addJoinedEntityFromClassMetadata(Travel::CLASSNAME, 't', 'c', 'travels', array('id' => 't_id')); - $uow->registerManaged($city, array('id' => $city->getId()), array('name' => $city->getName(), 'state' => null)); - - $this->queryCache->put($key, $rsm, $result); - } - /** * @expectedException Doctrine\ORM\Cache\CacheException * @expectedExceptionMessage Second level cache does not support scalar results. diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php index 927755f99..409dec663 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php @@ -23,6 +23,19 @@ class AnnotationDriverTest extends AbstractMappingDriverTest $annotationDriver->loadMetadataForClass('stdClass', $cm); } + /** + * @expectedException Doctrine\ORM\Cache\CacheException + * @expectedExceptionMessage Entity association field "Doctrine\Tests\ORM\Mapping\AnnotationSLC#foo" not configured as part of the second-level cache. + */ + public function testFailingSecondLevelCacheAssociation() + { + $className = 'Doctrine\Tests\ORM\Mapping\AnnotationSLC'; + $mappingDriver = $this->_loadDriver(); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + } + /** * @group DDC-268 */ @@ -351,3 +364,26 @@ class InvalidFetchOption */ private $collection; } + +/** + * @Entity + * @Cache + */ +class AnnotationSLC +{ + /** + * @Id + * @ManyToOne(targetEntity="AnnotationSLCFoo") + */ + public $foo; +} +/** + * @Entity + */ +class AnnotationSLCFoo +{ + /** + * @Column(type="string") + */ + public $id; +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php index 5d06983ca..142455542 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php @@ -33,4 +33,17 @@ class PHPMappingDriverTest extends AbstractMappingDriverTest { $this->createClassMetadata('Doctrine\Tests\Models\DDC889\DDC889Class'); } + + /** + * @expectedException Doctrine\ORM\Cache\CacheException + * @expectedExceptionMessage Entity association field "Doctrine\Tests\ORM\Mapping\PHPSLC#foo" not configured as part of the second-level cache. + */ + public function testFailingSecondLevelCacheAssociation() + { + $className = 'Doctrine\Tests\ORM\Mapping\PHPSLC'; + $mappingDriver = $this->_loadDriver(); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php index 3ca82d08d..c2822f178 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -32,6 +32,19 @@ class XmlMappingDriverTest extends AbstractMappingDriverTest $this->assertEquals($expectedMap, $class->discriminatorMap); } + /** + * @expectedException Doctrine\ORM\Cache\CacheException + * @expectedExceptionMessage Entity association field "Doctrine\Tests\ORM\Mapping\XMLSLC#foo" not configured as part of the second-level cache. + */ + public function testFailingSecondLevelCacheAssociation() + { + $className = 'Doctrine\Tests\ORM\Mapping\XMLSLC'; + $mappingDriver = $this->_loadDriver(); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + } + public function testIdentifierWithAssociationKey() { $driver = $this->_loadDriver(); @@ -176,3 +189,12 @@ class CTI class CTIFoo extends CTI {} class CTIBar extends CTI {} class CTIBaz extends CTI {} + +class XMLSLC +{ + public $foo; +} +class XMLSLCFoo +{ + public $id; +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php new file mode 100644 index 000000000..c9341958a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php @@ -0,0 +1,12 @@ +enableCache(array( + 'usage' => ClassMetadataInfo::CACHE_USAGE_READ_ONLY +)); +$metadata->mapManyToOne(array( + 'fieldName' => 'foo', + 'id' => true, + 'targetEntity' => 'PHPSLCFoo' +)); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.XMLSLC.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.XMLSLC.dcm.xml new file mode 100644 index 000000000..ca8092b50 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.XMLSLC.dcm.xml @@ -0,0 +1,13 @@ + + + + + + + + + +