Skip to content

Commit

Permalink
command / query types
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangut committed Oct 5, 2019
1 parent 53b3d35 commit a35a468
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 30 deletions.
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class CreateUserCommand extends AbstractCommand
In case of a command without any payload you could extend `Gears\CQRS\AbstractEmptyCommand`

```php
use Gears\CQRS\AbstractCommand;
use Gears\CQRS\AbstractEmptyCommand;

class CreateUserCommand extends AbstractEmptyCommand
{
Expand Down Expand Up @@ -96,6 +96,19 @@ class FindUserQuery extends AbstractQuery
}
```

In case of a query without any payload you could extend `Gears\CQRS\AbstractEmptyQuery`

```php
use Gears\CQRS\AbstractEmptyQuery;

class FindAllUsersQuery extends AbstractEmptyQuery
{
public static function instance(): self {
return new self();
}
}
```

### Handlers

Commands and Queries are handed over to `Gears\CQRS\CommandHandler` and `Gears\CQRS\QueryHandler` respectively on their corresponding buses
Expand All @@ -120,7 +133,24 @@ class CreateUserCommandHandler extends AbstractCommandHandler
$command->getBirthDate()
);

[...]
// ...
}
}

class FindUserQueryHandler extends AbstractQueryHandler
{
protected function getSupportedQueryType(): string
{
return FindUserQuery::class;
}

protected function handleCommand(Query $query): DTO
{
/* @var FindUserQuery $query */

// Retrieve user from persistence by it's name $query->getName()

return new UserDTO(/* parameters */);
}
}
```
Expand Down
12 changes: 11 additions & 1 deletion src/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,22 @@ final protected function __construct(array $parameters)
$this->setPayload($parameters);
}

/**
* {@inheritdoc}
*/
public function getCommandType(): string
{
return \get_called_class();
}

/**
* {@inheritdoc}
*/
final public static function reconstitute(array $parameters)
{
return new static($parameters);
$commandClass = \get_called_class();

return new $commandClass($parameters);
}

/**
Expand Down
10 changes: 5 additions & 5 deletions src/AbstractCommandHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ abstract class AbstractCommandHandler implements CommandHandler
*/
final public function handle(Command $command): void
{
$supportedCommandType = $this->getSupportedCommandType();
if (!\is_a($command, $supportedCommandType)) {
if ($command->getCommandType() !== $this->getSupportedCommandType()) {
throw new InvalidCommandException(\sprintf(
'Command must be a "%s", "%s" given',
$supportedCommandType,
\get_class($command)
'Command handler "%s" can only handle "%s" commands, "%s" given',
self::class,
$this->getSupportedCommandType(),
$command->getCommandType()
));
}

Expand Down
14 changes: 12 additions & 2 deletions src/AbstractEmptyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use Gears\Immutability\ImmutabilityBehaviour;

/**
* Abstract empty immutable event.
* Abstract empty immutable serializable command.
*/
abstract class AbstractEmptyCommand implements Command
{
Expand All @@ -33,14 +33,24 @@ final protected function __construct()
$this->assertImmutable();
}

/**
* {@inheritdoc}
*/
public function getCommandType(): string
{
return \get_called_class();
}

/**
* {@inheritdoc}
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
final public static function reconstitute(array $parameters)
{
return new static();
$commandClass = \get_called_class();

return new $commandClass();
}

/**
Expand Down
53 changes: 53 additions & 0 deletions src/AbstractEmptyQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* cqrs (https://github.com/phpgears/cqrs).
* CQRS base.
*
* @license MIT
* @link https://github.com/phpgears/cqrs
* @author Julián Gutiérrez <juliangut@gmail.com>
*/

declare(strict_types=1);

namespace Gears\CQRS;

use Gears\DTO\ScalarPayloadBehaviour;
use Gears\Immutability\ImmutabilityBehaviour;

/**
* Abstract empty immutable serializable query.
*/
abstract class AbstractEmptyQuery implements Query
{
use ImmutabilityBehaviour, ScalarPayloadBehaviour {
ScalarPayloadBehaviour::__call insteadof ImmutabilityBehaviour;
}

/**
* AbstractEmptyQuery constructor.
*/
final protected function __construct()
{
$this->assertImmutable();
}

/**
* {@inheritdoc}
*/
public function getQueryType(): string
{
return \get_called_class();
}

/**
* {@inheritdoc}
*
* @return string[]
*/
final protected function getAllowedInterfaces(): array
{
return [Query::class];
}
}
8 changes: 8 additions & 0 deletions src/AbstractQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ final protected function __construct(array $parameters)
$this->setPayload($parameters);
}

/**
* {@inheritdoc}
*/
public function getQueryType(): string
{
return \get_called_class();
}

/**
* {@inheritdoc}
*
Expand Down
10 changes: 5 additions & 5 deletions src/AbstractQueryHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ abstract class AbstractQueryHandler implements QueryHandler
*/
final public function handle(Query $query): DTO
{
$supportedQueryType = $this->getSupportedQueryType();
if (!\is_a($query, $supportedQueryType)) {
if ($query->getQueryType() !== $this->getSupportedQueryType()) {
throw new InvalidQueryException(\sprintf(
'Query command must be a "%s", "%s" given',
$supportedQueryType,
\get_class($query)
'Query handler "%s" can only handle "%s" queries, "%s" given',
self::class,
$this->getSupportedQueryType(),
$query->getQueryType()
));
}

Expand Down
7 changes: 7 additions & 0 deletions src/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
*/
interface Command
{
/**
* Get command type.
*
* @return string
*/
public function getCommandType(): string;

/**
* Check parameter existence.
*
Expand Down
7 changes: 7 additions & 0 deletions src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
*/
interface Query
{
/**
* Get query type.
*
* @return string
*/
public function getQueryType(): string;

/**
* Check parameter existence.
*
Expand Down
4 changes: 3 additions & 1 deletion tests/CQRS/AbstractCommandHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ class AbstractCommandHandlerTest extends TestCase
public function testInvalidCommandType(): void
{
$this->expectException(InvalidCommandException::class);
$this->expectExceptionMessageRegExp('/^Command must be a ".+\\\AbstractCommandStub", ".+" given$/');
$this->expectExceptionMessageRegExp(
'/^Command handler ".+" can only handle ".+\\\AbstractCommandStub" commands, ".+" given$/'
);

$handler = new AbstractCommandHandlerStub();
$handler->handle(AbstractEmptyCommandStub::instance());
Expand Down
25 changes: 11 additions & 14 deletions tests/CQRS/AbstractQueryHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

namespace Gears\CQRS\Tests;

use Gears\CQRS\AbstractQuery;
use Gears\CQRS\Exception\InvalidQueryException;
use Gears\CQRS\Tests\Stub\AbstractEmptyQueryStub;
use Gears\CQRS\Tests\Stub\AbstractQueryHandlerStub;
use Gears\CQRS\Tests\Stub\AbstractQueryStub;
use Gears\DTO\DTO;
Expand All @@ -25,24 +25,21 @@
*/
class AbstractQueryHandlerTest extends TestCase
{
public function testHandling(): void
{
$handler = new AbstractQueryHandlerStub();

static::assertInstanceOf(DTO::class, $handler->handle(AbstractQueryStub::instance()));
}

public function testInvalidQueryType(): void
{
$this->expectException(InvalidQueryException::class);
$this->expectExceptionMessageRegExp('/^Query command must be a ".+\\\AbstractQueryStub", ".+" given$/');
$this->expectExceptionMessageRegExp(
'/^Query handler ".+" can only handle ".+\\\AbstractQueryStub" queries, ".+" given$/'
);

/** @var AbstractQuery $query */
$query = $this->getMockBuilder(AbstractQuery::class)
->disableOriginalConstructor()
->getMock();
$handler = new AbstractQueryHandlerStub();
$handler->handle(AbstractEmptyQueryStub::instance());
}

public function testHandling(): void
{
$handler = new AbstractQueryHandlerStub();
$handler->handle($query);

static::assertInstanceOf(DTO::class, $handler->handle(AbstractQueryStub::instance()));
}
}
32 changes: 32 additions & 0 deletions tests/CQRS/Stub/AbstractEmptyQueryStub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* cqrs (https://github.com/phpgears/cqrs).
* CQRS base.
*
* @license MIT
* @link https://github.com/phpgears/cqrs
* @author Julián Gutiérrez <juliangut@gmail.com>
*/

declare(strict_types=1);

namespace Gears\CQRS\Tests\Stub;

use Gears\CQRS\AbstractEmptyQuery;

/**
* Abstract empty query stub class.
*/
class AbstractEmptyQueryStub extends AbstractEmptyQuery
{
/**
* Instantiate command.
*
* @return self
*/
public static function instance(): self
{
return new self();
}
}

0 comments on commit a35a468

Please sign in to comment.