Merge branch 'master' of github.com:doctrine/doctrine2
This commit is contained in:
commit
5733574867
@ -9,6 +9,7 @@ The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepo
|
||||
|
||||
The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way:
|
||||
|
||||
// new call to the AnnotationRegistry
|
||||
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
|
@ -499,7 +499,7 @@ class AnnotationDriver implements Driver
|
||||
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
),
|
||||
'/^.+\\' . $this->_fileExtension . '$/i',
|
||||
'/^.+' . str_replace('.', '\.', $this->_fileExtension) . '$/i',
|
||||
\RecursiveRegexIterator::GET_MATCH
|
||||
);
|
||||
|
||||
|
@ -633,7 +633,11 @@ class BasicEntityPersister
|
||||
// TRICKY: since the association is specular source and target are flipped
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
|
||||
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
// unset the old value and set the new sql aliased value here. By definition
|
||||
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
|
||||
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
|
||||
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
unset($identifier[$targetKeyColumn]);
|
||||
} else {
|
||||
throw MappingException::joinColumnMustPointToMappedField(
|
||||
$sourceClass->name, $sourceKeyColumn
|
||||
|
@ -274,6 +274,14 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
|
||||
{
|
||||
if (!$this->__isInitialized__ && $this->_entityPersister) {
|
||||
$this->__isInitialized__ = true;
|
||||
|
||||
if (method_exists($this, "__wakeup")) {
|
||||
// call this after __isInitialized__to avoid infinite recursion
|
||||
// but before loading to emulate what ClassMetadata::newInstance()
|
||||
// provides.
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
||||
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
|
||||
throw new \Doctrine\ORM\EntityNotFoundException();
|
||||
}
|
||||
|
151
lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php
Normal file
151
lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use Doctrine\ORM\Event\OnFlushEventArgs;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
/**
|
||||
* Use this logger to dump the identity map during the onFlush event. This is useful for debugging
|
||||
* weird UnitOfWork behavior with complex operations.
|
||||
*/
|
||||
class DebugUnitOfWorkListener
|
||||
{
|
||||
private $file;
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* Pass a stream and contet information for the debugging session.
|
||||
*
|
||||
* The stream can be php://output to print to the screen.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $context
|
||||
*/
|
||||
public function __construct($file = 'php://output', $context = '')
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function onFlush(OnFlushEventArgs $args)
|
||||
{
|
||||
$this->dumpIdentityMap($args->getEntityManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the contents of the identity map into a stream.
|
||||
*
|
||||
* @param EntityManager $em
|
||||
* @return void
|
||||
*/
|
||||
public function dumpIdentityMap(EntityManager $em)
|
||||
{
|
||||
$uow = $em->getUnitOfWork();
|
||||
$identityMap = $uow->getIdentityMap();
|
||||
|
||||
$fh = fopen($this->file, "x+");
|
||||
if (count($identityMap) == 0) {
|
||||
fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n");
|
||||
foreach ($identityMap AS $className => $map) {
|
||||
fwrite($fh, "Class: ". $className . "\n");
|
||||
foreach ($map AS $idHash => $entity) {
|
||||
fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n");
|
||||
fwrite($fh, " Associations:\n");
|
||||
|
||||
$cm = $em->getClassMetadata($className);
|
||||
foreach ($cm->associationMappings AS $field => $assoc) {
|
||||
fwrite($fh, " " . $field . " ");
|
||||
$value = $cm->reflFields[$field]->getValue($entity);
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if ($value === null) {
|
||||
fwrite($fh, " NULL\n");
|
||||
} else {
|
||||
if ($value instanceof Proxy && !$value->__isInitialized__) {
|
||||
fwrite($fh, "[PROXY] ");
|
||||
}
|
||||
|
||||
fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n");
|
||||
}
|
||||
} else {
|
||||
$initialized = !($value instanceof PersistentCollection) || $value->isInitialized();
|
||||
if ($value === null) {
|
||||
fwrite($fh, " NULL\n");
|
||||
} else if ($initialized) {
|
||||
fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n");
|
||||
foreach ($value AS $obj) {
|
||||
fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n");
|
||||
}
|
||||
} else {
|
||||
fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n");
|
||||
foreach ($value->unwrap() AS $obj) {
|
||||
fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
private function getType($var)
|
||||
{
|
||||
if (is_object($var)) {
|
||||
$refl = new \ReflectionObject($var);
|
||||
return $refl->getShortname();
|
||||
} else {
|
||||
return gettype($var);
|
||||
}
|
||||
}
|
||||
|
||||
private function getIdString($entity, $uow)
|
||||
{
|
||||
if ($uow->isInIdentityMap($entity)) {
|
||||
$ids = $uow->getEntityIdentifier($entity);
|
||||
$idstring = "";
|
||||
foreach ($ids AS $k => $v) {
|
||||
$idstring .= $k."=".$v;
|
||||
}
|
||||
} else {
|
||||
$idstring = "NEWOBJECT ";
|
||||
}
|
||||
|
||||
$state = $uow->getEntityState($entity);
|
||||
if ($state == UnitOfWork::STATE_NEW) {
|
||||
$idstring .= " [NEW]";
|
||||
} else if ($state == UnitOfWork::STATE_REMOVED) {
|
||||
$idstring .= " [REMOVED]";
|
||||
} else if ($state == UnitOfWork::STATE_MANAGED) {
|
||||
$idstring .= " [MANAGED]";
|
||||
} else if ($state == UnitOfwork::STATE_DETACHED) {
|
||||
$idstring .= " [DETACHED]";
|
||||
}
|
||||
|
||||
return $idstring;
|
||||
}
|
||||
}
|
@ -451,7 +451,7 @@ public function <methodName>()
|
||||
} else if ($token[0] == T_FUNCTION) {
|
||||
if ($tokens[$i+2][0] == T_STRING) {
|
||||
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
|
||||
} else if ($tokens[$i+2][0] == T_AMPERSAND && $tokens[$i+3][0] == T_STRING) {
|
||||
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
|
||||
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
|
||||
}
|
||||
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
|
||||
@ -691,6 +691,7 @@ public function <methodName>()
|
||||
if ($this->_hasMethod($methodName, $metadata)) {
|
||||
return;
|
||||
}
|
||||
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
|
||||
|
||||
$var = sprintf('_%sMethodTemplate', $type);
|
||||
$template = self::$$var;
|
||||
@ -723,6 +724,7 @@ public function <methodName>()
|
||||
if ($this->_hasMethod($methodName, $metadata)) {
|
||||
return;
|
||||
}
|
||||
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
|
||||
|
||||
$replacements = array(
|
||||
'<name>' => $this->_annotationsPrefix . $name,
|
||||
|
@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.1.0RC4-DEV';
|
||||
const VERSION = '2.2.0-DEV';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
@ -1 +1 @@
|
||||
Subproject commit 74a2c924cd08b30785877808b1fb519b4b2e60b1
|
||||
Subproject commit 40f1bf16e84ddc5291a6a63aa00b9879c40e3500
|
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
@ -1 +1 @@
|
||||
Subproject commit be3790059cc43b674a55548eb42d5d25846ea6a9
|
||||
Subproject commit 0127ee98a4301f2f6e3463c824adc3a3687f901f
|
@ -34,7 +34,7 @@ class ECommerceCustomer
|
||||
* only one customer at the time, while a customer can choose only one
|
||||
* mentor. Not properly appropriate but it works.
|
||||
*
|
||||
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
|
||||
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"}, fetch="EAGER")
|
||||
* @JoinColumn(name="mentor_id", referencedColumnName="id")
|
||||
*/
|
||||
private $mentor;
|
||||
|
@ -56,6 +56,7 @@ class ECommerceProduct
|
||||
private $related;
|
||||
|
||||
public $isCloned = false;
|
||||
public $wakeUp = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -166,4 +167,12 @@ class ECommerceProduct
|
||||
{
|
||||
$this->isCloned = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing docblock contents here
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->wakeUp = true;
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,14 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
|
||||
$this->assertForeignKeyIs(null);
|
||||
}
|
||||
|
||||
public function testFind()
|
||||
{
|
||||
$id = $this->_createFixture();
|
||||
|
||||
$customer = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $customer->getMentor());
|
||||
}
|
||||
|
||||
public function testEagerLoadsAssociation()
|
||||
{
|
||||
$this->_createFixture();
|
||||
@ -127,6 +135,8 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
return $customer->getId();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,4 +130,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
|
||||
$this->assertEquals('Doctrine 2 Cookbook', $entity->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1022
|
||||
*/
|
||||
public function testWakeupCalledOnProxy()
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
||||
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
|
||||
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
|
||||
|
||||
$this->assertFalse($entity->wakeUp);
|
||||
|
||||
$entity->setName('Doctrine 2 Cookbook');
|
||||
|
||||
$this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup().");
|
||||
}
|
||||
}
|
||||
|
96
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1250Test.php
Normal file
96
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1250Test.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Tests\Models\CMS\CmsEmployee;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1250
|
||||
*/
|
||||
class DDC1250Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1250ClientHistory'),
|
||||
));
|
||||
} catch(\PDOException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$c1 = new DDC1250ClientHistory;
|
||||
$c2 = new DDC1250ClientHistory;
|
||||
$c1->declinedClientsHistory = $c2;
|
||||
$c1->declinedBy = $c2;
|
||||
$c2->declinedBy = $c1;
|
||||
$c2->declinedClientsHistory= $c1;
|
||||
|
||||
$this->_em->persist($c1);
|
||||
$this->_em->persist($c2);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$history = $this->_em->createQuery('SELECT h FROM ' . __NAMESPACE__ . '\\DDC1250ClientHistory h WHERE h.id = ?1')
|
||||
->setParameter(1, $c2->id)->getSingleResult();
|
||||
|
||||
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC1250ClientHistory', $history);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1250ClientHistory
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
public $id;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC1250ClientHistory", inversedBy="declinedBy")
|
||||
* @JoinColumn(name="declined_clients_history_id", referencedColumnName="id")
|
||||
*/
|
||||
public $declinedClientsHistory;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="DDC1250ClientHistory", mappedBy="declinedClientsHistory")
|
||||
* @var
|
||||
*/
|
||||
public $declinedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
Entities\ClientsHistory:
|
||||
type: entity
|
||||
table: clients_history
|
||||
fields:
|
||||
id:
|
||||
id: true
|
||||
type: integer
|
||||
unsigned: false
|
||||
nullable: false
|
||||
generator:
|
||||
strategy: IDENTITY
|
||||
[...skiped...]
|
||||
oneToOne:
|
||||
declinedClientsHistory:
|
||||
targetEntity: Entities\ClientsHistory
|
||||
joinColumn:
|
||||
name: declined_clients_history_id
|
||||
referencedColumnName: id
|
||||
inversedBy: declinedBy
|
||||
declinedBy:
|
||||
targetEntity: Entities\ClientsHistory
|
||||
mappedBy: declinedClientsHistory
|
||||
lifecycleCallbacks: { }
|
||||
repositoryClass: Entities\ClientsHistoryRepository
|
||||
|
||||
|
||||
*/
|
@ -304,6 +304,10 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($GLOBALS['debug_uow_listener'])) {
|
||||
$evm->addEventListener(array('onFlush'), new \Doctrine\ORM\Tools\DebugUnitOfWorkListener());
|
||||
}
|
||||
|
||||
return \Doctrine\ORM\EntityManager::create($conn, $config);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user