Skip to content

Commit

Permalink
Merge pull request #7 from awd-studio/#2_invokable
Browse files Browse the repository at this point in the history
#2 Provided supporting for invokable handlers
  • Loading branch information
awd-studio committed Aug 4, 2019
2 parents b71b13d + 3109d63 commit d576607
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 66 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/lsp/
/build/
/vendor/
/composer.lock
Expand Down
35 changes: 26 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,18 @@ class MyCommandHandler
$this->anotherDependency = $anotherDependency;
}

// This method must be implemented by each command,
// otherwise, there will be thrown an exception.
// The bus looks if the handler is invokable and calls the
// "__invoke" method if it exists. Also, it can look for
// the method "handle" and calls it.
//
// Otherwise, if those methods aren't exists -
// the exception will be thrown.
//
// The command to handle will be sent as an argument.
//
// According to a canonical implementation of a command
// pattern, handlers shouldn't have return statement.
public function handle(MyCommand $command): void
public function __invoke(MyCommand $command): void
{
$foo = $command->foo;
$bar = $command->bar;
Expand Down Expand Up @@ -118,11 +124,17 @@ class MyQueryHandler
$this->anotherDependency = $anotherDependency;
}

// This method must be implemented by each command,
// otherwise, there will be thrown an exception.
// The bus looks if the handler is invokable and calls the
// "__invoke" method if it exists. Also, it can look for
// the methods "fetch" or "handle" and calls them.
//
// Otherwise, if those methods aren't exists -
// the exception will be thrown.
//
// The query to handle will be sent as an argument.
//
// It must return the result of a query execution.
public function fetch(MyQuery $query): ?MyEntity
public function __invoke(MyQuery $query): ?MyEntity
{
$foo = $query->foo;
$bar = $query->bar;
Expand Down Expand Up @@ -178,10 +190,15 @@ class MyEventSubscriber
$this->anotherDependency = $anotherDependency;
}

// This method must be implemented by each command,
// otherwise, there will be thrown an exception.
// The bus looks if the handler is invokable and calls the
// "__invoke" method if it exists. Also, it can look for
// the methods "notify" or "handle" and calls them.
//
// Otherwise, if those methods aren't exists -
// the exception will be thrown.
//
// The event to handle will be sent as an argument.
public function notify(MyEvent $event): void
public function __invoke(MyEvent $event): void
{
$foo = $event->foo;
$bar = $event->bar;
Expand Down
32 changes: 20 additions & 12 deletions src/ServiceBuses/CommandBus/CommandBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ public function subscribe(string $commandHandler, string $command): CommandBusIn
*/
public function handle($command): void
{
$id = $this->resolveHandler($command);
$handler = $this->container->get($id);
$this->validateHandler($handler);
$handler->handle($command);
$handlerId = $this->resolveHandlerId($command);
$handler = $this->container->get($handlerId);
$handlerMethod = $this->resolveHandlingMethod($handler);
$handler->{$handlerMethod}($command);
}

/**
Expand All @@ -58,7 +58,7 @@ public function handle($command): void
* @return string
* @throws \AwdStudio\ServiceBuses\CommandBus\Exception\CommandHandlerNotDefined
*/
private function resolveHandler($command): string
private function resolveHandlerId($command): string
{
$commandClass = \get_class($command);
$handlerClass = $this->handlers[$commandClass] ?? null;
Expand All @@ -72,20 +72,28 @@ private function resolveHandler($command): string
}

/**
* Checks if the handler contains a required method to execute handling.
* Checks if the handler contains a required method to execute handling and returns it's name.
*
* @param object $handler A handler to check.
*
* @return string The name of a handling method.
*
* @throws \AwdStudio\ServiceBuses\CommandBus\Exception\CommandHandlerIsInappropriate
*/
private function validateHandler($handler): void
private function resolveHandlingMethod($handler): string
{
if (!\method_exists($handler, 'handle')) {
throw new CommandHandlerIsInappropriate(\sprintf(
'The handler "%s" does not contains a required method "handle"',
\get_class($handler)
));
if (\method_exists($handler, '__invoke')) {
return '__invoke';
}

if (\method_exists($handler, 'handle')) {
return 'handle';
}

throw new CommandHandlerIsInappropriate(\sprintf(
'The handler "%s" must be invokable, or contain a required method "handle"',
\get_class($handler)
));
}

}
29 changes: 20 additions & 9 deletions src/ServiceBuses/EventBus/EventBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public function dispatch($event): void
$subscriberIds = $this->resolveSubscribers($event);
foreach ($subscriberIds as $subscriberId) {
$handler = $this->container->get($subscriberId);
$this->validateHandler($handler);
$handler->notify($event);
$handlingMethod = $this->resolveHandlingMethod($handler);
$handler->{$handlingMethod}($event);
}
}

Expand All @@ -71,20 +71,31 @@ private function resolveSubscribers($event): array
}

/**
* Checks if the handler contains a required method to execute handling.
* Checks if the handler contains a required method to execute handling and returns it's name.
*
* @param object $handler A handler to check.
*
* @return string The name of a handler method.
* @throws \AwdStudio\ServiceBuses\EventBus\Exception\EventSubscriberIsInappropriate
*/
private function validateHandler($handler): void
private function resolveHandlingMethod($handler): string
{
if (!\method_exists($handler, 'notify')) {
throw new EventSubscriberIsInappropriate(\sprintf(
'The subscriber "%s" does not contains a required method "notify"',
\get_class($handler)
));
if (\method_exists($handler, '__invoke')) {
return '__invoke';
}

if (\method_exists($handler, 'handle')) {
return 'handle';
}

if (\method_exists($handler, 'notify')) {
return 'notify';
}

throw new EventSubscriberIsInappropriate(\sprintf(
'The subscriber "%s" must contain one of required methods: "__invoke", "handle" or "notify""',
\get_class($handler)
));
}

}
30 changes: 21 additions & 9 deletions src/ServiceBuses/QueryBus/QueryBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public function handle($query)
{
$id = $this->resolveHandler($query);
$handler = $this->container->get($id);
$this->validateHandler($handler);
$handlingMethod = $this->resolveHandlingMethod($handler);

return $handler->fetch($query);
return $handler->{$handlingMethod}($query);
}

/**
Expand All @@ -73,20 +73,32 @@ private function resolveHandler($query): string
}

/**
* Checks if the handler contains a required method to execute handling.
* Checks if the handler contains a required method to execute handling and returns it's name.
*
* @param object $handler A handler to check.
*
* @return string The name of the handling method.
*
* @throws \AwdStudio\ServiceBuses\QueryBus\Exception\QueryHandlerIsNotAppropriate
*/
private function validateHandler($handler): void
private function resolveHandlingMethod($handler): string
{
if (!\method_exists($handler, 'fetch')) {
throw new QueryHandlerIsNotAppropriate(\sprintf(
'The handler "%s" does not contains a required method "fetch"',
\get_class($handler)
));
if (\method_exists($handler, '__invoke')) {
return '__invoke';
}

if (\method_exists($handler, 'handle')) {
return 'handle';
}

if (\method_exists($handler, 'fetch')) {
return 'fetch';
}

throw new QueryHandlerIsNotAppropriate(\sprintf(
'The handler "%s" must contain one of required methods: "__invoke", "handle" or "fetch"',
\get_class($handler)
));
}

}
49 changes: 42 additions & 7 deletions tests/Unit/ServiceBuses/CommandBus/CommandBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ public function testSubscribe()
/**
* @covers ::subscribe
* @covers ::handle
* @covers ::resolveHandler
* @covers ::validateHandler
* @covers ::resolveHandlerId
* @covers ::resolveHandlingMethod
*/
public function testSubscribeHandle()
public function testSubscribeAndInvoke()
{
// Sample handler
$handler = new class
Expand All @@ -70,7 +70,7 @@ public function testSubscribeHandle()
public $state = null;

// Required method
public function handle(\stdClass $command): void
public function __invoke(\stdClass $command): void
{
$this->state = $command;
}
Expand All @@ -87,13 +87,48 @@ public function handle(\stdClass $command): void

// Executing
$this->instance->handle(new \stdClass());
$this->assertInstanceOf(\stdClass::class, $handler->state);
}

/**
* @covers ::subscribe
* @covers ::handle
* @covers ::resolveHandlerId
* @covers ::resolveHandlingMethod
*/
public function testSubscribeAndHandle()
{
// Sample handler
$handler = new class
{
// We will check this state after handling
public $state = null;

// Required method
public function handle(\stdClass $command): void
{
$this->state = $command;
}
};

// Subscribe a handler to a sample command
$this->instance->subscribe(\get_class($handler), \stdClass::class);

// Feed a handler with a DI-container to the Bus
$this->container
->expects($this->any())
->method('get')
->willReturn($handler);

// Executing
$this->instance->handle(new \stdClass());
$this->assertInstanceOf(\stdClass::class, $handler->state);
}

/**
* @covers ::handle
* @covers ::resolveHandler
* @covers ::resolveHandlerId
* @covers ::resolveHandlingMethod
*/
public function testHandleWrongCommand()
{
Expand All @@ -105,8 +140,8 @@ public function testHandleWrongCommand()
/**
* @covers ::subscribe
* @covers ::handle
* @covers ::resolveHandler
* @covers ::validateHandler
* @covers ::resolveHandlerId
* @covers ::resolveHandlingMethod
*/
public function testHandleWrongHandler()
{
Expand Down
40 changes: 36 additions & 4 deletions tests/Unit/ServiceBuses/EventBus/EventBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ public function testSubscribe()
* @covers ::subscribe
* @covers ::dispatch
* @covers ::resolveSubscribers
* @covers ::validateHandler
* @covers ::resolveHandlingMethod
*/
public function testSubscribeHandle()
public function testSubscribeAndHandle()
{
// Sample subscriber
$subscriber1 = new class
Expand All @@ -69,7 +69,7 @@ public function testSubscribeHandle()
public $state = null;

// Required method
public function notify(\stdClass $event): void
public function __invoke(\stdClass $event): void
{
$this->state = 'Subscriber 1 is notified';
}
Expand All @@ -88,9 +88,36 @@ public function notify(\stdClass $event): void
}
};

// Sample subscriber
$subscriber2 = new class
{
// We will check this state after handling
public $state = null;

// Required method
public function handle(\stdClass $event): void
{
$this->state = 'Subscriber 2 is notified';
}
};

// Sample subscriber
$subscriber3 = new class
{
// We will check this state after handling
public $state = null;

// Required method
public function notify(\stdClass $event): void
{
$this->state = 'Subscriber 3 is notified';
}
};

// Subscribe subscribers to a sample command
$this->instance->subscribe(\get_class($subscriber1), \stdClass::class);
$this->instance->subscribe(\get_class($subscriber2), \stdClass::class);
$this->instance->subscribe(\get_class($subscriber3), \stdClass::class);

// Feed subscribers with a DI-container to the Bus
$this->container
Expand All @@ -101,19 +128,24 @@ public function notify(\stdClass $event): void
->expects($this->at(1))
->method('get')
->willReturn($subscriber2);
$this->container
->expects($this->at(2))
->method('get')
->willReturn($subscriber3);

// Executing
$this->instance->dispatch(new \stdClass());

$this->assertSame('Subscriber 1 is notified', $subscriber1->state);
$this->assertSame('Subscriber 2 is notified', $subscriber2->state);
$this->assertSame('Subscriber 3 is notified', $subscriber3->state);
}

/**
* @covers ::subscribe
* @covers ::dispatch
* @covers ::resolveSubscribers
* @covers ::validateHandler
* @covers ::resolveHandlingMethod
*/
public function testHandleWrongHandler()
{
Expand Down
Loading

0 comments on commit d576607

Please sign in to comment.