Installation:
composer require league/tactician-bundle
Add to config/bundles.php (Symfony 4+):
return [
// ...
League\Tactician\Bundle\TacticianBundle::class => ['all' => true],
];
First Command:
Define a command class (e.g., app/Commands/SendWelcomeEmail.php):
namespace App\Commands;
class SendWelcomeEmail implements Command
{
public $email;
public $userId;
public function __construct(string $email, int $userId)
{
$this->email = $email;
$this->userId = $userId;
}
}
Dispatch via Controller:
use League\Tactician\CommandBus;
class UserController
{
public function __construct(private CommandBus $commandBus) {}
public function register(Request $request)
{
$this->commandBus->handle(new SendWelcomeEmail(
$request->get('email'),
$request->get('user_id')
));
}
}
Register Command Handler:
services:
League\Tactician\Bundle\TacticianBundle\Handler\CommandHandler:
arguments:
- ['App\Commands\SendWelcomeEmail']
tags: ['tactician.command_handler']
RegisterUser command from controller.ValidateEmail) to check validity.RegisterUserHandler to persist data.Dependency Injection:
Inject CommandBus into services/controllers:
public function __construct(private CommandBus $bus) {}
Dispatch commands:
$this->bus->handle(new YourCommand($data));
Async Dispatch (Symfony Messenger):
Combine with symfony/messenger for async processing:
services:
League\Tactician\Bundle\TacticianBundle\Messenger\TacticianMiddleware:
arguments:
- '@command_bus'
tags: ['messenger.middleware']
Common Middleware:
LogCommand (logging)Validate (input validation)Authorize (permission checks)Retry (for transient failures)Custom Middleware:
use League\Tactician\Middleware;
class LogExecutionTime implements Middleware
{
public function execute($command, callable $next)
{
$start = microtime(true);
$result = $next($command);
$time = microtime(true) - $start;
// Log $time...
return $result;
}
}
Register in config:
tactician:
command_bus:
middleware:
- League\Tactician\Middleware\Chain
- App\Middleware\LogExecutionTime
Post-Command Events:
Use League\Tactician\Event\Handling\EventDispatcher to trigger events after command execution.
Example:
$dispatcher->dispatch(new CommandHandled($command, $result));
Integration with Symfony Events:
$eventDispatcher->addListener(
CommandHandled::class,
[$this, 'onCommandHandled']
);
Mock Command Bus:
$bus = $this->createMock(CommandBus::class);
$bus->expects($this->once())
->method('handle')
->with($this->isInstanceOf(YourCommand::class));
Isolated Handler Tests:
$handler = new YourCommandHandler($service);
$result = $handler->handle(new YourCommand($data));
$this->assertEquals($expected, $result);
Circular Dependencies:
CommandBus into command handlers (handlers should be stateless).Middleware Order Matters:
Authorize before Validate to fail fast.League\Tactician\Middleware\Chain to group related middleware.Command Bus Not Found:
TacticianBundle is enabled and command_bus is configured in config/packages/tactician.yaml.php bin/console debug:container command_bus to verify.Handler Not Registered:
tactician.command_handler or manually bind them in DI.services:
App\Handler\YourHandler:
tags: ['tactician.command_handler']
Log Command Execution:
Add League\Tactician\Middleware\LogCommand to middleware chain:
tactician:
command_bus:
middleware:
- League\Tactician\Middleware\LogCommand
Inspect Middleware:
Use League\Tactician\Middleware\DebugMiddleware for verbose logging:
tactician:
command_bus:
middleware:
- League\Tactician\Middleware\DebugMiddleware
Symfony Profiler:
Enable tactician.profiler in config to track commands in the profiler toolbar.
Custom Command Bus:
Extend League\Tactician\CommandBus for specialized behavior (e.g., priority queues):
class PriorityCommandBus extends CommandBus
{
protected function getMiddleware(): Middleware
{
return new Chain([
new PrioritizeMiddleware(),
parent::getMiddleware(),
]);
}
}
Dynamic Handler Mapping:
Use League\Tactician\Handler\Locator\MethodNameInflectorHandlerLocator for dynamic handler resolution:
tactician:
command_bus:
handler_locator: League\Tactician\Handler\Locator\MethodNameInflectorHandlerLocator
Integrate with Doctrine:
Use League\Tactician\Doctrine\ORM\TransactionMiddleware for transactional commands:
tactician:
command_bus:
middleware:
- League\Tactician\Doctrine\ORM\TransactionMiddleware
Batch Commands:
Use League\Tactician\CommandBus::handleMultiple() for bulk operations:
$this->bus->handleMultiple([
new Command1(),
new Command2(),
]);
Lazy-Load Handlers: For heavy handlers, lazy-load dependencies:
class LazyHandler implements CommandHandler
{
private $service;
public function handle(Command $command)
{
if (!$this->service) {
$this->service = new HeavyService();
}
// ...
}
}
Cache Middleware:
Cache results of idempotent commands using League\Tactician\Middleware\CacheCommandResultMiddleware.
How can I help you explore Laravel packages today?