Skip to content

Commit

Permalink
adding more organization in code and improvement use
Browse files Browse the repository at this point in the history
  • Loading branch information
dersonsena committed May 17, 2021
1 parent 175e921 commit 55a1883
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 38 deletions.
60 changes: 40 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This is a Yii Framework 2 Wrapper/Adapter for [Tactician Command Bus Library](ht
>
> The command bus itself is really easy to decorate with extra behaviors, like locking or database transactions so it’s very easy to extend with plugins.
>
> _By: tactician.thephpleague.com_
> By: tactician.thephpleague.com
## When should I not use it?

Expand All @@ -34,38 +34,56 @@ $ composer require dersonsena/yii2-tactician

## Setup

First of all, let's create a file with under `config/command-bus.php` to map our command and handlers ones, link this:
First of all, you must configure your [Yii Dependency Injection Container](https://www.yiiframework.com/doc/guide/2.0/en/concept-di-container) to be able to use Command and Handler classes inside him.

You should have something like that in your `config/web.php`:

```php
return [
YourCommandClass::class => YourHandlerClass::class,
AnotherCommandClass::class => AnotherHandlerClass::class,
// ...
$config = [
'id' => 'your-app-id',
//...
'container' => [
'definitions' => [
MyClassCommand::class => MyClassHandler::class
]
],
'components' => [
//...
]
];
```

**Heads up**: to become easily, this component will be call automatically the Handler classes, `YourHandlerClass` and `AnotherHandlerClass` in this example, from [Yii Dependency Injection Container](https://www.yiiframework.com/doc/guide/2.0/en/concept-di-container).
You must follow the conventions below:

- Your **Command** class must be suffixed by `Command`
- Your **Handler** class must be: `Same Command Class Name` + `Without Command Suffix` + `Handler`.

The last one is register component in your `config/web.php` file, as below:

```php
return [
// ...
$config = [
'id' => 'your-app-id',
//...
'container' => [
'definitions' => [
MyClassCommand::class => MyClassHandler::class
]
],
'components' => [
'commandBus' => [
'class' => DersonSena\Yii2Tactician\Yii2TacticianCommandBus::class,
'commandHandlerMap' => require __DIR__ . '/command-bus.php'
'class' => DersonSena\Yii2Tactician\Yii2TacticianCommandBus::class
],
// other components...
],
// ...
]
];
```

Define a command class somewhere in your application, for example:
## How to Use

Define a `Command` class somewhere in your application, for example:

```php
class YourCommandClass
class MyClassCommand
{
public $someParam;
public $someOtherParam;
Expand All @@ -78,26 +96,28 @@ class YourCommandClass
}
```

So your `Handler` class should be something like:
Define your `Handler` class should be something like:

```php
class YourCommandClassHandler
class MyClassHandler
{
public function handle(YourCommandClass $command)
public function handle(MyClassCommand $command)
{
// do command stuff hire!
// we can use $command->someParam and $this->someOtherParam
}
}
```

Now we can use this command in controllers (or wherever you want):
Now we can use this command in controllers or wherever you want:

```php
public function actionDoSomething()
{
$queryParam = Yii::$app->getRequest()->get('some_param');
$result = Yii::$app->commandBus->handle(new YourCommandClass($queryParam));

// Here the magic happens! =)
$result = Yii::$app->commandBus->handle(new MyClassCommand($queryParam));

if ($result === true) {
return $this->redirect(['go/to/some/place']);
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/ClassName/ClassNameInflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\ClassName;

interface ClassNameInflector
{
public function getClassName(string $commandClassName): string;
}
21 changes: 21 additions & 0 deletions src/ClassName/Suffix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\ClassName;

final class Suffix implements ClassNameInflector
{
private string $suffix;

public function __construct(string $suffix)
{
$this->suffix = $suffix;
}

public function getClassName(string $commandClassName): string
{
// Command class name without "Command" + Suffix (most of time "Handler")
return str_replace('Command', '', $commandClassName) . $this->suffix;
}
}
28 changes: 28 additions & 0 deletions src/CommandHandlerMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician;

use League\Tactician\Middleware;
use yii\di\Container;

final class CommandHandlerMiddleware implements Middleware
{
private Container $container;
private CommandToHandlerMapping $mapping;

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

public function execute($command, callable $next)
{
$methodToCall = $this->mapping->mapCommandToHandler(get_class($command));
$handler = $this->container->get($methodToCall->getClassName());

return $handler->{$methodToCall->getMethodName()}($command);
}
}
13 changes: 13 additions & 0 deletions src/CommandToHandlerMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace DersonSena\Yii2Tactician;

use DersonSena\Yii2Tactician\Exceptions\FailedToMapCommand;

interface CommandToHandlerMapping
{
/**
* @throws FailedToMapCommand
*/
public function mapCommandToHandler(string $commandFQCN): MethodToCall;
}
20 changes: 20 additions & 0 deletions src/Exceptions/FailedToMapCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\Exceptions;

use Exception;

final class FailedToMapCommand extends Exception
{
public static function className(string $commandClassName): self
{
return new static('Failed to map the class name for command ' . $commandClassName);
}

public static function methodName(string $commandClassName): self
{
return new static('Failed to map the method name for command ' . $commandClassName);
}
}
41 changes: 41 additions & 0 deletions src/Exceptions/MethodDoesNotExist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\Exceptions;

use BadMethodCallException;
use League\Tactician\Exception\Exception;

final class MethodDoesNotExist extends BadMethodCallException implements Exception
{
private string $className;
private string $methodName;

private function __construct(string $message, string $className, string $methodName)
{
parent::__construct($message);
$this->className = $className;
$this->methodName = $methodName;
}

public static function on(string $className, string $methodName): self
{
return new self(
'The handler method ' . $className . '::' . $methodName . ' does not exist. Please check your Tactician ' .
'mapping configuration or check verify that ' . $methodName . ' is actually declared in . ' . $className,
$className,
$methodName
);
}

public function getClassName(): string
{
return $this->className;
}

public function getMethodName(): string
{
return $this->methodName;
}
}
17 changes: 17 additions & 0 deletions src/Exceptions/MissingHandleMethodException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\Exceptions;

use Exception;
use Throwable;

final class MissingHandleMethodException extends Exception
{
public function __construct($code = 0, Throwable $previous = null)
{
$message = "You must have a 'handle' method in your Handler Class";
parent::__construct($message, $code, $previous);
}
}
30 changes: 30 additions & 0 deletions src/MapByNamingConvention.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician;

use DersonSena\Yii2Tactician\ClassName\ClassNameInflector;
use DersonSena\Yii2Tactician\MethodName\MethodNameInflector;

final class MapByNamingConvention implements CommandToHandlerMapping
{
private ClassNameInflector $classNameInflector;
private MethodNameInflector $methodNameInflector;

public function __construct(
ClassNameInflector $classNameInflector,
MethodNameInflector $methodNameInflector
) {
$this->classNameInflector = $classNameInflector;
$this->methodNameInflector = $methodNameInflector;
}

public function mapCommandToHandler(string $commandFQCN): MethodToCall
{
return new MethodToCall(
$this->classNameInflector->getClassName($commandFQCN),
$this->methodNameInflector->getMethodName($commandFQCN)
);
}
}
13 changes: 13 additions & 0 deletions src/MethodName/Handle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\MethodName;

final class Handle implements MethodNameInflector
{
public function getMethodName(string $commandClassName): string
{
return 'handle';
}
}
10 changes: 10 additions & 0 deletions src/MethodName/MethodNameInflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician\MethodName;

interface MethodNameInflector
{
public function getMethodName(string $commandClassName): string;
}
33 changes: 33 additions & 0 deletions src/MethodToCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace DersonSena\Yii2Tactician;

use DersonSena\Yii2Tactician\Exceptions\MethodDoesNotExist;

final class MethodToCall
{
private string $className;
private string $methodName;

public function __construct(string $className, string $methodName)
{
if (!method_exists($className, $methodName) && ! method_exists($className, '__call')) {
throw MethodDoesNotExist::on($className, $methodName);
}

$this->className = $className;
$this->methodName = $methodName;
}

public function getClassName(): string
{
return $this->className;
}

public function getMethodName(): string
{
return $this->methodName;
}
}
Loading

0 comments on commit 55a1883

Please sign in to comment.