Merge branch 'DDC-692'
This commit is contained in:
commit
e9c4f612cf
11 changed files with 106 additions and 5 deletions
|
@ -88,6 +88,7 @@
|
||||||
<xs:attribute name="repository-class" type="xs:string"/>
|
<xs:attribute name="repository-class" type="xs:string"/>
|
||||||
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
|
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
|
||||||
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
|
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
|
||||||
|
<xs:attribute name="read-only" type="xs:boolean" default="false" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="mapped-superclass" >
|
<xs:complexType name="mapped-superclass" >
|
||||||
|
|
|
@ -334,6 +334,10 @@ class ClassMetadata extends ClassMetadataInfo
|
||||||
$serialized[] = 'namedQueries';
|
$serialized[] = 'namedQueries';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isReadOnly) {
|
||||||
|
$serialized[] = 'isReadOnly';
|
||||||
|
}
|
||||||
|
|
||||||
return $serialized;
|
return $serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -484,6 +484,17 @@ class ClassMetadataInfo implements ClassMetadata
|
||||||
*/
|
*/
|
||||||
public $reflClass;
|
public $reflClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this entity marked as "read-only"?
|
||||||
|
*
|
||||||
|
* That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
|
||||||
|
* optimization for entities that are immutable, either in your domain or through the relation database
|
||||||
|
* (coming from a view, or a history table for example).
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $isReadOnly = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
||||||
* metadata of the class with the given name.
|
* metadata of the class with the given name.
|
||||||
|
@ -1818,4 +1829,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||||
{
|
{
|
||||||
$this->versionField = $versionField;
|
$this->versionField = $versionField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this class as read only, no change tracking is applied to it.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function markReadOnly()
|
||||||
|
{
|
||||||
|
$this->isReadOnly = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,10 @@ class AnnotationDriver implements Driver
|
||||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||||
|
|
||||||
|
if ($entityAnnot->readOnly) {
|
||||||
|
$metadata->markReadOnly();
|
||||||
|
}
|
||||||
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
||||||
$metadata->isMappedSuperclass = true;
|
$metadata->isMappedSuperclass = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
/*
|
/*
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
@ -27,6 +25,7 @@ use Doctrine\Common\Annotations\Annotation;
|
||||||
|
|
||||||
final class Entity extends Annotation {
|
final class Entity extends Annotation {
|
||||||
public $repositoryClass;
|
public $repositoryClass;
|
||||||
|
public $readOnly = false;
|
||||||
}
|
}
|
||||||
final class MappedSuperclass extends Annotation {}
|
final class MappedSuperclass extends Annotation {}
|
||||||
final class InheritanceType extends Annotation {}
|
final class InheritanceType extends Annotation {}
|
||||||
|
|
|
@ -55,6 +55,9 @@ class XmlDriver extends AbstractFileDriver
|
||||||
$metadata->setCustomRepositoryClass(
|
$metadata->setCustomRepositoryClass(
|
||||||
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
|
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
|
||||||
);
|
);
|
||||||
|
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
|
||||||
|
$metadata->markReadOnly();
|
||||||
|
}
|
||||||
} else if ($xmlRoot->getName() == 'mapped-superclass') {
|
} else if ($xmlRoot->getName() == 'mapped-superclass') {
|
||||||
$metadata->isMappedSuperclass = true;
|
$metadata->isMappedSuperclass = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -49,6 +49,9 @@ class YamlDriver extends AbstractFileDriver
|
||||||
$metadata->setCustomRepositoryClass(
|
$metadata->setCustomRepositoryClass(
|
||||||
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
|
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
|
||||||
);
|
);
|
||||||
|
if (isset($element['readOnly']) && $element['readOnly'] == true) {
|
||||||
|
$metadata->markReadOnly();
|
||||||
|
}
|
||||||
} else if ($element['type'] == 'mappedSuperclass') {
|
} else if ($element['type'] == 'mappedSuperclass') {
|
||||||
$metadata->isMappedSuperclass = true;
|
$metadata->isMappedSuperclass = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -514,9 +514,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||||
$class = $this->em->getClassMetadata($className);
|
$class = $this->em->getClassMetadata($className);
|
||||||
|
|
||||||
// Skip class if instances are read-only
|
// Skip class if instances are read-only
|
||||||
//if ($class->isReadOnly) {
|
if ($class->isReadOnly) {
|
||||||
// continue;
|
continue;
|
||||||
//}
|
}
|
||||||
|
|
||||||
// If change tracking is explicit or happens through notification, then only compute
|
// If change tracking is explicit or happens through notification, then only compute
|
||||||
// changes on entities of that type that are explicitly marked for synchronization.
|
// changes on entities of that type that are explicitly marked for synchronization.
|
||||||
|
|
|
@ -59,6 +59,7 @@ class AllTests
|
||||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\PostgreSQLIdentityStrategyTest');
|
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\PostgreSQLIdentityStrategyTest');
|
||||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ExtraLazyCollectionTest');
|
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ExtraLazyCollectionTest');
|
||||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ClearEventTest');
|
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ClearEventTest');
|
||||||
|
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ReadOnlyTest');
|
||||||
|
|
||||||
$suite->addTest(Locking\AllTests::suite());
|
$suite->addTest(Locking\AllTests::suite());
|
||||||
$suite->addTest(Ticket\AllTests::suite());
|
$suite->addTest(Ticket\AllTests::suite());
|
||||||
|
|
61
tests/Doctrine/Tests/ORM/Functional/ReadOnlyTest.php
Normal file
61
tests/Doctrine/Tests/ORM/Functional/ReadOnlyTest.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Functional;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Query tests.
|
||||||
|
*
|
||||||
|
* @group DDC-692
|
||||||
|
*/
|
||||||
|
class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->_schemaTool->createSchema(array(
|
||||||
|
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadOnlyEntityNeverChangeTracked()
|
||||||
|
{
|
||||||
|
$readOnly = new ReadOnlyEntity("Test1", 1234);
|
||||||
|
$this->_em->persist($readOnly);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$readOnly->name = "Test2";
|
||||||
|
$readOnly->number = 4321;
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$dbReadOnly = $this->_em->find('Doctrine\Tests\ORM\Functional\ReadOnlyEntity', $readOnly->id);
|
||||||
|
$this->assertEquals("Test1", $dbReadOnly->name);
|
||||||
|
$this->assertEquals(1234, $dbReadOnly->number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity(readOnly=true)
|
||||||
|
*/
|
||||||
|
class ReadOnlyEntity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Id @GeneratedValue @Column(type="integer")
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
/** @column(type="string") */
|
||||||
|
public $name;
|
||||||
|
/** @Column(type="integer") */
|
||||||
|
public $number;
|
||||||
|
|
||||||
|
public function __construct($name, $number)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->number = $number;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
||||||
$cm->setCustomRepositoryClass("UserRepository");
|
$cm->setCustomRepositoryClass("UserRepository");
|
||||||
$cm->setDiscriminatorColumn(array('name' => 'disc', 'type' => 'integer'));
|
$cm->setDiscriminatorColumn(array('name' => 'disc', 'type' => 'integer'));
|
||||||
$cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'Bar', 'mappedBy' => 'foo'));
|
$cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'Bar', 'mappedBy' => 'foo'));
|
||||||
|
$cm->markReadOnly();
|
||||||
|
$cm->addNamedQuery(array('name' => 'dql', 'query' => 'foo'));
|
||||||
$this->assertEquals(1, count($cm->associationMappings));
|
$this->assertEquals(1, count($cm->associationMappings));
|
||||||
|
|
||||||
$serialized = serialize($cm);
|
$serialized = serialize($cm);
|
||||||
|
@ -51,6 +53,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
||||||
$this->assertTrue($oneOneMapping['fetch'] == ClassMetadata::FETCH_LAZY);
|
$this->assertTrue($oneOneMapping['fetch'] == ClassMetadata::FETCH_LAZY);
|
||||||
$this->assertEquals('phonenumbers', $oneOneMapping['fieldName']);
|
$this->assertEquals('phonenumbers', $oneOneMapping['fieldName']);
|
||||||
$this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']);
|
$this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']);
|
||||||
|
$this->assertTrue($cm->isReadOnly);
|
||||||
|
$this->assertEquals(array('dql' => 'foo'), $cm->namedQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFieldIsNullable()
|
public function testFieldIsNullable()
|
||||||
|
|
Loading…
Add table
Reference in a new issue