Skip to content

Commit

Permalink
Merge pull request #36 from biig-io/fix/execution-order-of-post-persist
Browse files Browse the repository at this point in the history
Definitive fix for execution order in post persist rules
  • Loading branch information
babeou authored Jul 18, 2018
2 parents a8755a0 + ce733ac commit 06e5241
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 19 deletions.
104 changes: 88 additions & 16 deletions Tests/Event/DelayedListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
use Biig\Component\Domain\Event\DomainEventDispatcher;
use Biig\Component\Domain\Model\DomainModel;
use Biig\Component\Domain\Model\Instantiator\DoctrineConfig\ClassMetadataFactory;
use Biig\Component\Domain\PostPersistListener\DoctrinePostPersistListener;
use Biig\Component\Domain\Rule\PostPersistDomainRuleInterface;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
use PHPUnit\Framework\TestCase;

class DelayedListenerTest extends TestCase
{
private $dbPath;

public function testICanInstantiateDelayedListener()
{
$delayedListener = new DelayedListener('foo', function () {});
Expand Down Expand Up @@ -57,23 +60,13 @@ public function testItFailsToRegisterOtherThanCurrentModel()

public function testItInsertInBddAfterFlushing()
{
$tmpPath = \sys_get_temp_dir() . '/testItInsertInBddAfterFlushing.' . \microtime() . '.sqlite';
copy(__DIR__ . '/../fixtures/dbtest/fake_model.db', $tmpPath);

$config = Setup::createYAMLMetadataConfiguration(array(__DIR__ . '/../fixtures/config'), true);
$config->setClassMetadataFactoryName(ClassMetadataFactory::class);
$conn = [
'driver' => 'pdo_sqlite',
'path' => $tmpPath,
];
$entityManager = EntityManager::create($conn, $config);
$dispatcher = new DomainEventDispatcher();
$entityManager = $this->setupDatabase($dispatcher);

$model = new \FakeModel();
$model->setFoo('Model 1');
$dispatcher = new DomainEventDispatcher();
$model->setFoo('Model1');
$model->setDispatcher($dispatcher);

$entityManager->getMetadataFactory()->setDispatcher($dispatcher);
$rule = new class($entityManager) implements PostPersistDomainRuleInterface {
private $entityManager;

Expand All @@ -97,16 +90,95 @@ public function execute(\Biig\Component\Domain\Event\DomainEvent $event)
};
$dispatcher->addRule($rule);

$model->doAction();
$entityManager->persist($model);
$entityManager->flush($model);

// 3 because the database was already containing 1 entry
$this->assertEquals(3, count($entityManager->getRepository(\FakeModel::class)->findAll()));
$this->dropDatabase();
}

public function testItDoesNotExecuteManyTimesSameEvent()
{
// Test setup
$dispatcher = new DomainEventDispatcher();
$entityManager = $this->setupDatabase($dispatcher);

$model = new \FakeModel();
$model->setFoo(0);
$model->setDispatcher($dispatcher);

$rule = new CountAndInsertRule($entityManager);
$dispatcher->addRule($rule);

// Test: the rule should be trigger 2 times
$model->doAction();
$model->doAction();
$dispatcher->persistModel($model);
$entityManager->persist($model);
$entityManager->flush($model);

$this->assertEquals(2, $model->getFoo());
$this->dropDatabase();
}

private function setupDatabase(DomainEventDispatcher $dispatcher)
{
$this->dbPath = \sys_get_temp_dir() . '/testItInsertInBddAfterFlushing.' . \microtime() . '.sqlite';
copy(__DIR__ . '/../fixtures/dbtest/fake_model.db', $this->dbPath);

$config = Setup::createYAMLMetadataConfiguration(array(__DIR__ . '/../fixtures/config'), true);
$config->setClassMetadataFactoryName(ClassMetadataFactory::class);
$conn = [
'driver' => 'pdo_sqlite',
'path' => $this->dbPath,
];

$entityManager = EntityManager::create($conn, $config);
$entityManager->getEventManager()->addEventSubscriber(new DoctrinePostPersistListener($dispatcher));

$entityManager->getMetadataFactory()->setDispatcher($dispatcher);

return $entityManager;
}

private function dropDatabase()
{
if (!$this->dbPath) {
return;
}

$this->assertEquals(count($entityManager->getRepository(\FakeModel::class)->findAll()), 3);
@unlink($tmpPath);
@unlink($this->dbPath);
}
}

class FakeDomainModel extends DomainModel
{
}

class CountAndInsertRule implements PostPersistDomainRuleInterface
{
private $entityManager;

public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}

public function after()
{
return [\FakeModel::class => 'action'];
}

public function execute(\Biig\Component\Domain\Event\DomainEvent $event)
{
// Count times of execution
$event->getSubject()->setFoo($event->getSubject()->getFoo() + 1);

// Trigger flush
$model = new \FakeModel();
$model->setFoo('Something new to insert');
$this->entityManager->persist($model);
$this->entityManager->flush($model);
}
}
15 changes: 12 additions & 3 deletions src/Event/DelayedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,20 @@ public function occur(DomainEvent $event)
*/
public function process(ModelInterface $model)
{
foreach ($this->eventStack as $key => $event) {
$tmpStack = $this->eventStack;
$this->eventStack = [];
$eventStack = [];

foreach ($tmpStack as $event) {
if (spl_object_hash($event->getSubject()) === spl_object_hash($model)) {
unset($this->eventStack[$key]);
\call_user_func($this->listener, $event);
$eventStack[] = $event;
continue;
}
$this->eventStack[] = $event;
}

foreach ($eventStack as $key => $event) {
\call_user_func($this->listener, $event);
}
}

Expand Down

0 comments on commit 06e5241

Please sign in to comment.