Installation:
composer require amine-lejmi/messenger-maker
For non-Flex projects, add the bundle to config/bundles.php:
AmineLejmi\MessengerMaker\MessengerMakerBundle::class => ['all' => true],
Configure Services (config/services.yaml):
services:
_instanceof:
AmineLejmi\MessengerMaker\Contract\CommandHandlerInterface:
tags: ['{ name: messenger.message_handler, bus: command.bus }']
# Repeat for QueryHandlerInterface and EventHandlerInterface as needed
Generate Your First Message:
php bin/console make:messenger:command SendEmailCommand
Follow prompts to define properties (e.g., address, message) and transport priority.
Verify Auto-Generated Files:
src/Messenger/Command/SendEmailCommand.php (message class with getters).src/Messenger/CommandHandler/SendEmailCommandHandler.php (handler implementing CommandHandlerInterface).Dispatch the Command (e.g., in a controller):
use Symfony\Component\Messenger\MessageBusInterface;
public function __construct(private MessageBusInterface $commandBus) {}
public function sendEmail(string $address, ?string $message): void
{
$this->commandBus->dispatch(new SendEmailCommand($address, $message));
}
Command/Query/Event Generation:
Use make:messenger:command, make:messenger:query, or make:messenger:event to scaffold messages with interactive prompts for properties and transports.
Example:
php bin/console make:messenger:query GetUserQuery
Prompt: Define properties like userId: int and select transport (e.g., sync).
Handler Implementation:
Implement the auto-generated interface (CommandHandlerInterface, etc.) in the handler class. The bundle ensures handlers are auto-registered with the correct bus (command.bus, query.bus, or event.bus).
Transport Routing:
The bundle auto-updates config/packages/messenger.yaml to route messages to the selected transport (e.g., high-priority, sync).
Bus Configuration:
Define buses in config/packages/messenger.yaml:
framework:
messenger:
buses:
command.bus: ~
query.bus: ~
event.bus: ~
Use command.bus for commands, query.bus for queries (with return types), and event.bus for events.
Dependency Injection: Inject the appropriate bus into services:
public function __construct(
private MessageBusInterface $commandBus,
private AsyncBusInterface $eventBus
) {}
Middleware:
Attach middleware to buses (e.g., logging, validation) in messenger.yaml:
framework:
messenger:
buses:
command.bus:
middleware:
- 'doctrine_transaction'
Unit Testing Handlers: Mock the message bus and verify handler invocations:
$bus = $this->createMock(MessageBusInterface::class);
$handler = new SendEmailCommandHandler();
$bus->expects($this->once())->method('dispatch');
$handler($command); // Assert logic in handler
Integration Testing:
Use Symfony’s MessengerTestTrait or mock transports to test message flow:
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
$serializer = $this->createMock(SerializerInterface::class);
$this->bus->setSerializer($serializer);
CQRS Separation:
Organize messages by domain (e.g., src/Messenger/User/Command/, src/Messenger/User/Query/).
Example:
php bin/console make:messenger:command User/DeleteUserCommand
DTOs for Complex Messages:
For messages with nested data, use DTOs (e.g., UserProfileDTO) and pass them as properties:
class UpdateUserProfileCommand {
public function __construct(private UserProfileDTO $profile) {}
}
Event Sourcing: Dispatch domain events after command handling:
$this->eventBus->dispatch(new UserProfileUpdatedEvent($userId, $profile));
Transport Misconfiguration:
messenger.yaml or selecting an unconfigured transport during message creation.messenger.yaml:
framework:
messenger:
transports:
high-priority: { dsn: 'sync://' }
Re-run the make command to reselect a valid transport.Handler Interface Mismatch:
CommandHandlerInterface for commands) or missing the _instanceof tag in services.yaml.services:
_instanceof:
AmineLejmi\MessengerMaker\Contract\CommandHandlerInterface:
tags: ['{ name: messenger.message_handler, bus: command.bus }']
Circular Dependencies:
class SendEmailCommandHandler implements CommandHandlerInterface {
public function __construct(private EmailService $emailService) {}
}
Message Routing Overrides:
messenger.yaml conflicting with auto-generated routes.framework:
messenger:
routing:
'App\Messenger\Command\SendEmailCommand': ~ # Override or exclude
Property Type Errors:
DateTime without DateTimeImmutable) causing generation failures.DateTimeImmutable, array, object) or extend the bundle’s type system (see Extension Points).Check Generated Files:
src/Messenger/ with correct namespaces and interfaces.Validate messenger.yaml:
php bin/console debug:config messenger to inspect routing and transport configurations.Enable Messenger Debugging:
messenger.yaml:
framework:
messenger:
buses:
command.bus:
middleware:
- 'debug'
bin/console messenger:consume async -vv.Handler Invocation Issues:
bin/console debug:container to verify handlers are registered with the correct bus.$handler = new SendEmailCommandHandler();
$handler(new SendEmailCommand('test@example.com', 'Hello'));
Transport Priority:
high-priority, low-priority) must match configured transports. Defaults to sync if none selected.Nullable Fields:
false. Explicitly answer yes to prompts for nullable properties.Custom Property Types:
string, int, bool, etc.). For custom types (e.g., User), ensure they are autowireable or use FQCNs:
New property name: user
Field type: App\Entity\User
Custom Message Templates:
vendor/amine-lejmi/messenger-maker/src/Resources/skeleton/ to config/skeleton/messenger-maker/.Additional Property Types:
Type class and updating the bundle’s TypeRegistry (requires fork/modification).Custom Handlers:
MessengerMakerEvents::POST_CREATE_MESSAGE to modify generated messages:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use AmineLejmi\MessengerMaker\Event\MessengerMakerEvents;
class CustomMessengerMakerSubscriber implements EventSubscriberInterface {
public static function get
How can I help you explore Laravel packages today?