Skip to content

Commit

Permalink
Add examples directory
Browse files Browse the repository at this point in the history
  • Loading branch information
rosstuck committed Dec 21, 2014
1 parent 3b7a4da commit 604f6cf
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 0 deletions.
37 changes: 37 additions & 0 deletions examples/1-beginner-standard-usage.php
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);
73 changes: 73 additions & 0 deletions examples/2-intermediate-decorate-command-bus.php
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.
*/
48 changes: 48 additions & 0 deletions examples/3-intermediate-custom-naming-conventions.php
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);
52 changes: 52 additions & 0 deletions examples/4-advanced-custom-handler-loading.php
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);
82 changes: 82 additions & 0 deletions examples/5-advanced-self-executing-commands.php
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.
*/
30 changes: 30 additions & 0 deletions examples/repeated-sample-code.php
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()
);

0 comments on commit 604f6cf

Please sign in to comment.