Install the Bundle
composer require gos/pubsub-router-bundle
Register the bundle in config/bundles.php:
return [
// ...
Gos\PubSubRouterBundle\GosPubSubRouterBundle::class => ['all' => true],
];
Configure Routes
Define routes in config/packages/gos_pub_sub_router.yaml:
gos_pub_sub_router:
routes:
user_created:
channel: 'user.created'
handler: 'App\Handler\UserCreatedHandler'
method: 'handle'
order_processed:
channel: 'order.processed'
handler: 'App\Handler\OrderProcessedHandler'
method: 'handle'
First Use Case: Subscribing to a Channel Use the router to subscribe to a channel (e.g., in a Symfony command or event listener):
use Gos\PubSubRouterBundle\Router\PubSubRouter;
class SubscribeToUserEventsCommand extends Command
{
public function __construct(private PubSubRouter $router)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->router->subscribe('user.created', function ($message) use ($output) {
$output->writeln('User created: ' . $message['data']);
});
return Command::SUCCESS;
}
}
Channel-to-Handler Mapping Use the router to dynamically dispatch messages to handlers:
$router->route('user.created', ['data' => 'John Doe']);
// Automatically invokes `App\Handler\UserCreatedHandler::handle()`.
Middleware Pipeline Attach middleware to routes for pre/post-processing:
gos_pub_sub_router:
routes:
user_created:
channel: 'user.created'
handler: 'App\Handler\UserCreatedHandler'
middleware:
- 'App\Middleware\LogUserAction'
- 'App\Middleware\ValidatePayload'
Dynamic Route Generation Generate channel names from route definitions:
$channel = $router->generate('user_created'); // Returns 'user.created'
Event-Driven Architecture
Subscribe to channels in Symfony events (e.g., KernelEvents::CONTROLLER):
public function onKernelController(ControllerEvent $event)
{
$this->router->subscribe('order.processed', function ($message) {
// Handle order processing logic.
});
}
Testing Mock the router in tests to verify handler execution:
$router = $this->createMock(PubSubRouter::class);
$router->expects($this->once())
->method('route')
->with('user.created', ['data' => 'John Doe']);
Asynchronous Processing Combine with Symfony Messenger for async handlers:
gos_pub_sub_router:
routes:
user_created:
channel: 'user.created'
handler: 'App\Message\UserCreatedMessage'
transport: 'async'
Handler Autowiring
Ensure handlers are properly autowired in Symfony’s container. Use #[Autowire] or services.yaml:
services:
App\Handler\UserCreatedHandler:
tags: ['gos_pub_sub_router.handler']
Circular Dependencies Avoid circular dependencies between handlers and the router. Prefer dependency injection over static calls.
Channel Naming Collisions
Use namespaced channels (e.g., app.user.created) to avoid conflicts with third-party packages.
Route Matching Issues Verify routes are loaded by dumping the router’s configuration:
$router->getRoutes(); // Returns array of configured routes.
Handler Not Found
Check for typos in handler or method keys in yaml. Enable debug mode to log unmatched routes:
gos_pub_sub_router:
debug: true
Middleware Errors Isolate middleware failures by testing them individually:
$middleware = new LogUserAction();
$middleware->process($message, function () use ($message) {
// Simulate next middleware or handler.
});
Custom Route Loaders
Extend route loading by implementing RouteLoaderInterface:
class DynamicRouteLoader implements RouteLoaderInterface
{
public function load(): array
{
return [
'dynamic_route' => [
'channel' => 'dynamic.channel',
'handler' => 'App\Handler\DynamicHandler',
],
];
}
}
Register it in services.yaml:
services:
App\Loader\DynamicRouteLoader:
tags: ['gos_pub_sub_router.route_loader']
Channel Prefixes Add prefixes to all channels via configuration:
gos_pub_sub_router:
channel_prefix: 'app.'
Now user.created becomes app.user.created.
Handler Factories Use factories for dynamic handler instantiation:
$router->setHandlerFactory(function ($handlerClass) {
return new $handlerClass(); // Or custom logic.
});
How can I help you explore Laravel packages today?