-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
|
||
use Tactician\Handler\MethodNameInflector\HandleClassNameInflector; | ||
use Tactician\Handler\Locator\InMemoryLocator; | ||
|
||
// Our example Command and Handler. /////////////////////////////////////////// | ||
class RegisterUserCommand | ||
{ | ||
public $emailAddress; | ||
public $password; | ||
} | ||
|
||
class RegisterUserHandler | ||
{ | ||
public function handleRegisterUserCommand(RegisterUserCommand $command) | ||
{ | ||
// Do your core application logic here. Don't actually echo stuff. :) | ||
echo "User {$command->emailAddress} was registered!\n"; | ||
} | ||
} | ||
|
||
// Setup the bus, normally in your DI container /////////////////////////////// | ||
$locator = new InMemoryLocator(); | ||
$locator->addHandler(new RegisterUserHandler(), RegisterUserCommand::class); | ||
|
||
$commandBus = new Tactician\CommandBus\ExecutingCommandBus( | ||
$locator, | ||
new HandleClassNameInflector() | ||
); | ||
|
||
// Controller Code //////////////////////////////////////////////////////////// | ||
$command = new RegisterUserCommand(); | ||
$command->emailAddress = '[email protected]'; | ||
$command->password = 'secret'; | ||
|
||
$commandBus->execute($command); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
require __DIR__ . '/repeated-sample-code.php'; | ||
|
||
use Tactician\CommandBus\CommandBus; | ||
|
||
/** | ||
* Let's say we want something happen every time we execute a command. | ||
* | ||
* Maybe we start a database transaction or maybe it's just write something to | ||
* a debug log. We can do this easily by writing a decorator around our main | ||
* command bus. | ||
*/ | ||
|
||
// Very simple logger example. | ||
class Logger { | ||
public function log($info) { echo "LOG: $info\n"; } | ||
} | ||
|
||
// Our Logging CommandBus. It takes the "main" command bus in the constructor | ||
// and passes the command onto it for execution, while it only cares about | ||
// doing the logging part. | ||
class LoggingCommandBus implements CommandBus | ||
{ | ||
protected $innerCommandBus; | ||
|
||
protected $logger; | ||
|
||
public function __construct(CommandBus $innerCommandBus, Logger $logger) | ||
{ | ||
$this->innerCommandBus = $innerCommandBus; | ||
$this->logger = $logger; | ||
} | ||
|
||
public function execute($command) | ||
{ | ||
$commandClass = get_class($command); | ||
|
||
$this->logger->log("Starting $commandClass"); | ||
$returnValue = $this->innerCommandBus->execute($command); | ||
$this->logger->log("$commandClass finished without errors"); | ||
|
||
return $returnValue; | ||
} | ||
} | ||
|
||
// Now we wrap the main command bus in our new logging command bus and | ||
// all of our Command operations in our app are instantly logged, hooray! | ||
$commandBus = new LoggingCommandBus($commandBus, new Logger()); | ||
|
||
// Controller Code | ||
$command = new RegisterUserCommand(); | ||
$command->emailAddress = '[email protected]'; | ||
$command->password = 'secret'; | ||
|
||
$commandBus->execute($command); | ||
|
||
/** | ||
* So, logging is very simple but this decorator concept is useful for lots of | ||
* things! Imagine writing decorators for: | ||
* - database transactions | ||
* - validation, | ||
* - error handling | ||
* - locking | ||
* - user permissions (can they send this command at all?) | ||
* - Anything you want! | ||
* | ||
* Even better, decorators are really easy to unit test! | ||
* | ||
* There's one final advantage: when we setup the decorators, we can control | ||
* the execution order. Want the logger to fire before db transaction? Sure, | ||
* just move it ahead in the decorator stack. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
require __DIR__ . '/repeated-sample-code.php'; | ||
|
||
/** | ||
* Tactician is meant to be very customizable, it makes no assumptions about | ||
* your code. | ||
* | ||
* For example, let's say you don't like that your handler methods need to be | ||
* called "handleNameOfYourCommand" and you'd rather it always use a method | ||
* named "handle". | ||
* | ||
* We can write a custom MethodNameInflector for that: | ||
*/ | ||
|
||
use Tactician\Handler\MethodNameInflector\MethodNameInflector; | ||
|
||
class MyCustomInflector implements MethodNameInflector | ||
{ | ||
// You can use the command and commandHandler to generate any name you | ||
// prefer but here, we'll always return the same one. | ||
public function inflect($command, $commandHandler) | ||
{ | ||
return 'handle'; | ||
} | ||
} | ||
|
||
// And we'll create a new handler with the method name we prefer to invoke | ||
class NewRegisterUserHandler | ||
{ | ||
public function handle($command) | ||
{ | ||
echo "See, Tactician now calls the handle method we prefer!\n"; | ||
} | ||
} | ||
|
||
// Now let's create our command bus again, but this time using our custom | ||
// method naming strategy | ||
use Tactician\CommandBus\ExecutingCommandBus; | ||
$locator->addHandler(new NewRegisterUserHandler(), RegisterUserCommand::class); | ||
$commandBus = new ExecutingCommandBus($locator, new MyCustomInflector()); | ||
|
||
// Controller Code time! | ||
$command = new RegisterUserCommand(); | ||
$command->emailAddress = '[email protected]'; | ||
$command->password = 'secret'; | ||
|
||
$commandBus->execute($command); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
require __DIR__ . '/repeated-sample-code.php'; | ||
|
||
/** | ||
* Let's also say you don't want to instantiate every handler at startup but | ||
* you'd rather pull them out of some Symfony/Zend/Laravel DI container, maybe | ||
* with a cool tags setup or some sort of id inflection. | ||
* | ||
* We can create a custom HandlerLocator for that. | ||
*/ | ||
use Tactician\Handler\Locator\HandlerLocator; | ||
class ContainerBasedHandlerLocator implements HandlerLocator | ||
{ | ||
protected $container; | ||
|
||
public function __construct($container) | ||
{ | ||
$this->container = $container; | ||
} | ||
|
||
public function getHandlerForCommand($command) | ||
{ | ||
// This is a cheesy naming strategy but it's just an example | ||
$handlerId = 'app.handler.' . get_class($command); | ||
return $this->container->get($handlerId); | ||
} | ||
} | ||
|
||
// Just a fake container: use the Symfony/Zend/Laravel one in real life. | ||
$fakeContainer = Mockery::mock(); | ||
$fakeContainer | ||
->shouldReceive('get') | ||
->with('app.handler.RegisterUserCommand') | ||
->andReturn(new RegisterUserHandler()); | ||
|
||
// Now, we create our command bus using our container based loader instead | ||
use Tactician\CommandBus\ExecutingCommandBus; | ||
use Tactician\Handler\MethodNameInflector\HandleClassNameInflector; | ||
|
||
$commandBus = new ExecutingCommandBus( | ||
new ContainerBasedHandlerLocator($fakeContainer), | ||
new HandleClassNameInflector() | ||
); | ||
|
||
// Controller Code: even though we've changed out the whole loading system we | ||
// will still see that user was registered. | ||
$command = new RegisterUserCommand(); | ||
$command->emailAddress = '[email protected]'; | ||
$command->password = 'secret'; | ||
|
||
$commandBus->execute($command); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
|
||
/** | ||
* If you're working with a very constrained domain where there's not so many | ||
* dependencies, you could skip handlers altogether and implement a more | ||
* classic version of the command pattern where commands execute themselves. | ||
* | ||
* Here's a Tactician version of the wikipedia Light Switch example. | ||
*/ | ||
interface SelfExecutingCommand { | ||
public function execute(Light $light); | ||
} | ||
|
||
class Light | ||
{ | ||
public $switchedOn = false; | ||
|
||
public function __toString() { | ||
$status = $this->switchedOn ? 'on' : 'off'; | ||
return "Light is {$status}\n"; | ||
} | ||
} | ||
|
||
class SwitchOff implements SelfExecutingCommand | ||
{ | ||
public function execute(Light $light) | ||
{ | ||
$light->switchedOn = false; | ||
} | ||
} | ||
|
||
class SwitchOn implements SelfExecutingCommand | ||
{ | ||
public function execute(Light $light) | ||
{ | ||
$light->switchedOn = true; | ||
} | ||
} | ||
|
||
use Tactician\CommandBus\CommandBus; | ||
class SelfExecutingCommandBus implements CommandBus | ||
{ | ||
protected $light; | ||
|
||
public function __construct($light) | ||
{ | ||
$this->light = $light; | ||
} | ||
|
||
public function execute($command) | ||
{ | ||
if (!$command instanceof SelfExecutingCommand) { | ||
throw InvalidArgumentException("Can not execute command"); | ||
} | ||
|
||
return $command->execute($this->light); | ||
} | ||
} | ||
|
||
$light = new Light(); | ||
$commandBus = new SelfExecutingCommandBus($light); | ||
echo $light; | ||
|
||
$commandBus->execute(new SwitchOn()); | ||
echo $light; | ||
|
||
$commandBus->execute(new SwitchOff()); | ||
echo $light; | ||
|
||
/** | ||
* So, this highly contrived example might smack of the famous SimplePHPEasyPlus | ||
* but it can still be useful in larger apps or libraries. | ||
* | ||
* You might also notice that we've used nothing of Tactician here except an | ||
* interface: without Handlers, we don't need a MethodNameInflector or a | ||
* HandlerLocator at all. | ||
* | ||
* But using only the interface is still really helpful: decorators and plugins | ||
* designed to work with Tactician can still be dropped onto our custom command | ||
* without any changes at all. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
require __DIR__ . '/../vendor/autoload.php'; | ||
|
||
// The basic code from example 1 that we reuse in future examples. | ||
|
||
use Tactician\Handler\MethodNameInflector\HandleClassNameInflector; | ||
use Tactician\Handler\Locator\InMemoryLocator; | ||
|
||
class RegisterUserCommand | ||
{ | ||
public $emailAddress; | ||
public $password; | ||
} | ||
|
||
class RegisterUserHandler | ||
{ | ||
public function handleRegisterUserCommand(RegisterUserCommand $command) | ||
{ | ||
// Do your core application logic here. Don't actually echo things. :) | ||
echo "User {$command->emailAddress} was registered!\n"; | ||
} | ||
} | ||
|
||
$locator = new InMemoryLocator(); | ||
$locator->addHandler(new RegisterUserHandler(), RegisterUserCommand::class); | ||
|
||
$commandBus = new Tactician\CommandBus\ExecutingCommandBus( | ||
$locator, | ||
new HandleClassNameInflector() | ||
); |