Skip to content

Commit

Permalink
Events: support event manager
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed Jul 11, 2023
1 parent e4e232a commit 52b85b8
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 30 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"contributte/tester": "^0.3",
"contributte/phpstan": "^0.1",
"contributte/console": "^0.9.1",
"nettrine/orm": "^0.9.0",
"mockery/mockery": "^1.3.5",
"psr/log": "^3.0",
"tracy/tracy": "^2.6.2"
Expand Down
5 changes: 3 additions & 2 deletions src/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Nettrine\DBAL;

use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
Expand Down Expand Up @@ -34,14 +35,14 @@ public function __construct(array $typesConfig = [], array $typesMapping = [])
/**
* @param mixed[] $params
*/
public function createConnection(array $params, ?Configuration $config = null): Connection
public function createConnection(array $params, ?Configuration $config = null, ?EventManager $em = null): Connection
{
if (!$this->initialized) {
$this->initializeTypes();
}

/** @phpstan-ignore-next-line */
$connection = DriverManager::getConnection($params, $config);
$connection = DriverManager::getConnection($params, $config, $em);
$platform = $connection->getDatabasePlatform();

foreach ($this->typesMapping as $dbType => $doctrineType) {
Expand Down
23 changes: 23 additions & 0 deletions src/DI/DbalExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Nettrine\DBAL\DI;

use Doctrine\Common\Cache\Cache;
use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Nette\DI\CompilerExtension;
Expand All @@ -12,9 +13,11 @@
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use Nettrine\DBAL\ConnectionFactory;
use Nettrine\DBAL\Events\ContainerEventManager;
use Nettrine\DBAL\Middleware\TracyMiddleware;
use Nettrine\DBAL\Tracy\BlueScreen\DbalBlueScreen;
use Nettrine\DBAL\Tracy\QueryPanel\QueryPanel;
use ReflectionClass;
use stdClass;
use Tracy\Bar;
use Tracy\BlueScreen;
Expand Down Expand Up @@ -123,6 +126,10 @@ public function loadConnectionConfiguration(): void
$builder = $this->getContainerBuilder();
$connectionConfig = $this->config->connection;

// Event manager
$builder->addDefinition($this->prefix('eventManager'))
->setFactory(ContainerEventManager::class);

// Connection factory
$builder->addDefinition($this->prefix('connectionFactory'))
->setFactory(ConnectionFactory::class, [$connectionConfig['types'], $connectionConfig['typesMapping']]);
Expand All @@ -133,6 +140,7 @@ public function loadConnectionConfiguration(): void
->setFactory($this->prefix('@connectionFactory') . '::createConnection', [
$connectionConfig,
$this->prefix('@configuration'),
$this->prefix('@eventManager'),
]);
}

Expand All @@ -150,6 +158,21 @@ public function beforeCompile(): void
array_keys($builder->findByTag(self::TAG_MIDDLEWARE))
),
]);

/** @var ServiceDefinition $eventManager */
$eventManager = $builder->getDefinition($this->prefix('eventManager'));

foreach ($builder->findByType(EventSubscriber::class) as $serviceName => $serviceDef) {
/** @var class-string<EventSubscriber> $serviceClass */
$serviceClass = (string) $serviceDef->getType();
$rc = new ReflectionClass($serviceClass);

/** @var EventSubscriber $subscriber */
$subscriber = $rc->newInstanceWithoutConstructor();
$events = $subscriber->getSubscribedEvents();

$eventManager->addSetup('?->addServiceSubscriber(?, ?)', ['@self', $events, $serviceName]);
}
}

public function afterCompile(ClassType $class): void
Expand Down
48 changes: 48 additions & 0 deletions src/Events/ContainerEventManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);

namespace Nettrine\DBAL\Events;

use Doctrine\Common\EventManager;
use Nette\DI\Container;

class ContainerEventManager extends EventManager
{

protected Container $container;

public function __construct(Container $container)
{
$this->container = $container;
}

/**
* @param string|string[] $events
*/
public function addServiceSubscriber(string|array $events, string $service): void
{
$this->addEventListener($events, new class($this->container, $service) {

public function __construct(
private readonly Container $container,
private readonly string $service
)
{
}

/**
* @param mixed[] $arguments
*/
public function __call(string $name, array $arguments): mixed
{
$subscriber = $this->container->getByName($this->service);

$callback = [$subscriber, $name];
assert(is_callable($callback));

return call_user_func_array($callback, $arguments);
}

});
}

}
77 changes: 77 additions & 0 deletions tests/Cases/DI/DbalExtension.events.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php declare(strict_types = 1);

namespace Tests\Cases\E2E;

use Contributte\Tester\Toolkit;
use Contributte\Tester\Utils\ContainerBuilder;
use Contributte\Tester\Utils\Neonkit;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Events;
use Nette\DI\Compiler;
use Nettrine\Cache\DI\CacheExtension;
use Nettrine\DBAL\DI\DbalExtension;
use Tester\Assert;
use Tests\Toolkit\Tests;
use Tracy\Bridges\Nette\TracyExtension;

require_once __DIR__ . '/../../bootstrap.php';

// Empty events
Toolkit::test(function (): void {
$container = ContainerBuilder::of()
->withCompiler(static function (Compiler $compiler): void {
$compiler->addExtension('nettrine.dbal', new DbalExtension());
$compiler->addExtension('nettrine.cache', new CacheExtension());
$compiler->addConfig([
'parameters' => [
'tempDir' => Tests::TEMP_PATH,
'appDir' => Tests::APP_PATH,
],
]);
$compiler->addConfig(Neonkit::load(<<<'NEON'
nettrine.dbal:
connection:
driver: pdo_sqlite
NEON
));
})->build();

/** @var EventManager $eventManager */
$eventManager = $container->getByType(EventManager::class);

Assert::count(0, $eventManager->getAllListeners());
});

// Subscriber
Toolkit::test(function (): void {
$container = ContainerBuilder::of()
->withCompiler(static function (Compiler $compiler): void {
$compiler->addExtension('nettrine.dbal', new DbalExtension());
$compiler->addExtension('nettrine.cache', new CacheExtension());
$compiler->addExtension('nette.tracy', new TracyExtension());
$compiler->addConfig([
'parameters' => [
'tempDir' => Tests::TEMP_PATH,
'appDir' => Tests::APP_PATH,
],
]);
$compiler->addConfig(Neonkit::load(<<<'NEON'
nettrine.dbal:
connection:
driver: pdo_sqlite
types:
foo: { class: Doctrine\DBAL\Types\StringType }
bar: Doctrine\DBAL\Types\IntegerType
services:
- Tests\Fixtures\Events\DummyOnClearSubscriber
NEON
));
})->build();

/** @var EventManager $eventManager */
$eventManager = $container->getByType(EventManager::class);

Assert::count(1, $eventManager->getAllListeners());
Assert::count(1, $eventManager->getAllListeners()[Events::onClear]);
});
52 changes: 52 additions & 0 deletions tests/Cases/Events/ContainerEventManager.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types = 1);

namespace Tests\Cases\Events;

use Contributte\Tester\Toolkit;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\OnClearEventArgs;
use Doctrine\ORM\Events;
use Mockery;
use Nette\DI\Container;
use Nettrine\DBAL\Events\ContainerEventManager;
use Tester\Assert;
use Tests\Fixtures\Events\DummyOnClearSubscriber;

require_once __DIR__ . '/../../bootstrap.php';

Toolkit::test(function (): void {
$entityManager = Mockery::mock(EntityManager::class);

$container = new Container();
$eventManager = new ContainerEventManager($container);

$subscriber = new DummyOnClearSubscriber();
$eventManager->addEventSubscriber($subscriber);

$event = new OnClearEventArgs($entityManager, 'foo');

Assert::count(0, $subscriber->events);
$eventManager->dispatchEvent(Events::onClear, $event);
Assert::count(1, $subscriber->events);

Assert::same([$event], $subscriber->events);
});

Toolkit::test(function (): void {
$entityManager = Mockery::mock(EntityManager::class);
$subscriber = new DummyOnClearSubscriber();

$container = new Container();
$container->addService('dummySubscriber', $subscriber);
$eventManager = new ContainerEventManager($container);

$eventManager->addServiceSubscriber(Events::onClear, 'dummySubscriber');

$event = new OnClearEventArgs($entityManager, 'foo');

Assert::count(0, $subscriber->events);
$eventManager->dispatchEvent(Events::onClear, $event);
Assert::count(1, $subscriber->events);

Assert::same([$event], $subscriber->events);
});
28 changes: 28 additions & 0 deletions tests/Fixtures/Events/DummyOnClearSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures\Events;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnClearEventArgs;
use Doctrine\ORM\Events;

final class DummyOnClearSubscriber implements EventSubscriber
{

/** @var OnClearEventArgs[] */
public array $events = [];

public function onClear(OnClearEventArgs $args): void
{
$this->events[] = $args;
}

/**
* @return string[]
*/
public function getSubscribedEvents(): array
{
return [Events::onClear];
}

}
28 changes: 0 additions & 28 deletions tests/Fixtures/Subscriber/PostConnectSubscriber.php

This file was deleted.

0 comments on commit 52b85b8

Please sign in to comment.