Commit a1a80938 authored by romanb's avatar romanb

[2.0] First draft for onFlush event.

parent 7badced1
...@@ -249,7 +249,7 @@ class Parser ...@@ -249,7 +249,7 @@ class Parser
$name = implode('\\', $nameParts); $name = implode('\\', $nameParts);
} }
// If it really an annotation class? // Is it really an annotation class?
if ( if (
(! $this->_isNestedAnnotation && $this->_lexer->lookahead != null && (! $this->_isNestedAnnotation && $this->_lexer->lookahead != null &&
! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) &&
......
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
/** /**
* Lifecycle Events are triggered by the UnitOfWork * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
* of entities.
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com * @link www.doctrine-project.com
......
<?php
/*
* $Id$
*
* 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\Event;
/**
* Provides event arguments for the preFlush event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class OnFlushEventArgs extends \Doctrine\Common\EventArgs
{
/**
* @var EntityManager
*/
private $_em;
//private $_entitiesToPersist = array();
//private $_entitiesToRemove = array();
public function __construct($em)
{
$this->_em = $em;
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->_em;
}
/*
public function addEntityToPersist($entity)
{
}
public function addEntityToRemove($entity)
{
}
public function addEntityToUpdate($entity)
{
}
public function getEntitiesToPersist()
{
return $this->_entitiesToPersist;
}
*/
}
\ No newline at end of file
...@@ -108,4 +108,15 @@ final class Events ...@@ -108,4 +108,15 @@ final class Events
* @var string * @var string
*/ */
const loadClassMetadata = 'loadClassMetadata'; const loadClassMetadata = 'loadClassMetadata';
/**
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
* after any changes to managed entities have been determined but before any
* actual database operations are executed. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the onFlush event is not raised.
*
* @var string
*/
const onFlush = 'onFlush';
} }
\ No newline at end of file
...@@ -339,7 +339,7 @@ class StandardEntityPersister ...@@ -339,7 +339,7 @@ class StandardEntityPersister
if (isset($this->_class->associationMappings[$field])) { if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field]; $assocMapping = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column. // Only owning side of x-1 associations can have a FK column.
if ( ! $assocMapping->isOneToOne() || ! $assocMapping->isOwningSide) { if ( ! $assocMapping->isOwningSide || ! $assocMapping->isOneToOne()) {
continue; continue;
} }
......
...@@ -271,6 +271,11 @@ class UnitOfWork implements PropertyChangedListener ...@@ -271,6 +271,11 @@ class UnitOfWork implements PropertyChangedListener
} }
} }
// Raise onFlush
if ($this->_evm->hasListeners(Events::onFlush)) {
$this->_evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->_em));
}
// Now we need a commit order to maintain referential integrity // Now we need a commit order to maintain referential integrity
$commitOrder = $this->_getCommitOrder(); $commitOrder = $this->_getCommitOrder();
...@@ -945,7 +950,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -945,7 +950,8 @@ class UnitOfWork implements PropertyChangedListener
} }
/** /**
* Registers a deleted entity. * INTERNAL:
* Schedules an entity for deletion.
* *
* @param object $entity * @param object $entity
*/ */
...@@ -2064,4 +2070,34 @@ class UnitOfWork implements PropertyChangedListener ...@@ -2064,4 +2070,34 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates[$oid] = $entity; $this->_entityUpdates[$oid] = $entity;
} }
} }
/**
* Gets the currently scheduled entity insertions in this UnitOfWork.
*
* @return array
*/
public function getScheduledEntityInsertions()
{
return $this->_entityInsertions;
}
/**
* Gets the currently scheduled entity updates in this UnitOfWork.
*
* @return array
*/
public function getScheduledEntityUpdates()
{
return $this->_entityUpdates;
}
/**
* Gets the currently scheduled entity deletions in this UnitOfWork.
*
* @return array
*/
public function getScheduledEntityDeletions()
{
return $this->_entityDeletions;
}
} }
...@@ -106,7 +106,7 @@ DOCBLOCK; ...@@ -106,7 +106,7 @@ DOCBLOCK;
*/ */
public function testAnnotationNamespaceAlias() public function testAnnotationNamespaceAlias()
{ {
$parser = new Parser; $parser = $this->createTestParser();
$parser->setAnnotationNamespaceAlias('Doctrine\Tests\Common\Annotations\\', 'alias'); $parser->setAnnotationNamespaceAlias('Doctrine\Tests\Common\Annotations\\', 'alias');
$docblock = <<<DOCBLOCK $docblock = <<<DOCBLOCK
/** /**
......
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* FlushEventTest
*
* @author robo
*/
class FlushEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
}
public function testPersistNewEntitiesOnPreFlush()
{
//$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);
$this->_em->getEventManager()->addEventListener(Events::onFlush, new OnFlushListener);
$user = new CmsUser;
$user->username = 'romanb';
$user->name = 'Roman';
$user->status = 'Dev';
$this->_em->persist($user);
$this->assertEquals(0, $user->phonenumbers->count());
$this->_em->flush();
$this->assertEquals(1, $user->phonenumbers->count());
$this->assertTrue($this->_em->contains($user->phonenumbers->get(0)));
$this->assertTrue($user->phonenumbers->get(0)->getUser() === $user);
$this->assertFalse($user->phonenumbers->isDirty());
// Can be used together with SQL Logging to check that a subsequent flush has
// nothing to do. This proofs the correctness of the changes that happened in onFlush.
//echo "SECOND FLUSH";
//$this->_em->flush();
}
}
class OnFlushListener
{
public function onFlush(OnFlushEventArgs $args)
{
//echo "---preFlush".PHP_EOL;
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof CmsUser) {
// Adds a phonenumber to every newly persisted CmsUser ...
$phone = new CmsPhonenumber;
$phone->phonenumber = 12345;
// Update object model
$entity->addPhonenumber($phone);
// Invoke regular persist call
$em->persist($phone);
// Explicitly calculate the changeset since onFlush is raised
// after changeset calculation!
$uow->computeChangeSet($em->getClassMetadata(get_class($phone)), $phone);
// Take a snapshot because the UoW wont do this for us, because
// the UoW did not visit this collection.
// Alternatively we could provide an ->addVisitedCollection() method
// on the UoW.
$entity->getPhonenumbers()->takeSnapshot();
}
/*foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
list ($old, $new) = $change;
var_dump($old);
}*/
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment