I have this command
<?php
namespace AppTodoApplicationCommand;
use RamseyUuidUuid;
use RamseyUuidUuidInterface;
class CreateTodoCommand
{
public function __construct(
private string $name,
private string $text,
private string $userId
) { }
public function getName(): string
{
return $this->name;
}
public function getText(): string
{
return $this->text;
}
public function getUserId(): UuidInterface
{
return Uuid::fromString($this->userId);
}
}
and this handler
<?php
namespace AppTodoApplicationCommand;
use AppTodoApplicationInterfacesCommandHandlerInterface;
use AppTodoDomainEntityTodo;
use AppTodoDomainRepositoryTodoRepositoryInterface;
use AppTodoDomainValueObjectName;
use AppTodoDomainValueObjectText;
use AppTodoDomainValueObjectUserId;
use RamseyUuidUuid;
class CreateTodoCommandHandler implements CommandHandlerInterface
{
public function __construct(private TodoRepositoryInterface $todoRepository)
{}
public function __invoke(CreateTodoCommand $command)
{
$todo = new Todo(
Uuid::uuid4(),
new Name($command->getName()),
new Text($command->getTe xt()),
new UserId($command->getUserId())
);
$this->todoRepository->save($todo);
}
}
this messenger.yaml
configuration
framework:
messenger:
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
# failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(RABBITMQ_DSN)%'
retry_strategy:
max_retries: 5
delay: 1000
multiplier: 2
max_delay: 60000
# failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
routing:
# Route your messages to the transports
# 'AppMessageYourMessage': async
'AppTodoApplicationCommandCreateTodoCommand': async
and this services.yaml
config
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# Registra el repositorio como servicio
AppTodoInfrastructureRepositoryDoctrineTodoRepository:
arguments:
$em: '@doctrine.orm.entity_manager'
# Registra el handler y autowire la interfaz con su implementación
AppTodoApplicationCommandCreateTodoCommandHandler:
arguments:
$todoRepository: '@AppTodoInfrastructureRepositoryDoctrineTodoRepository'
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
But when I try t consume the queued messages I get this error
messenger.CRITICAL: Error thrown while handling message AppTodoApplicationCommandCreateTodoCommand. Removing from transport after 5 retries. Error: "No handler for message "AppTodoApplicationCommandCreateTodoCommand"." {"class":"App\Todo\Application\Command\CreateTodoCommand","retryCount":5,"error":"No handler for message "App\Todo\Application\Command\CreateTodoCommand".","exception":"[object] (Symfony\Component\Messenger\Exception\NoHandlerForMessageException(code: 0): No handler for message "App\Todo\Application\Command\CreateTodoCommand". at /var/www/html/vendor/symfony/messenger/Middleware/HandleMessageMiddleware.php:117)"} []
If I check the handlers with bin/console debug:messenger
there is no CreateTodoCommandHandler
on the list
Messenger
=========
messenger.bus.default
---------------------
The following messages can be dispatched:
----------------------------------------------------------
SymfonyComponentProcessMessengerRunProcessMessage
handled by process.messenger.process_message_handler
SymfonyComponentConsoleMessengerRunCommandMessage
handled by console.messenger.execute_command_handler
SymfonyComponentMessengerMessageRedispatchMessage
handled by messenger.redispatch_message_handler
----------------------------------------------------------
And I cleared cache several times
I think the config is correct, but there is no way to set the handler, there is something wrong or do I missed something?
If I use an attribute
use SymfonyComponentMessengerAttributeAsMessageHandler;
#[AsMessageHandler]
class CreateTodoCommandHandler implements CommandHandlerInterface
then everything work correctly.
It’s a handler registration problem, and I need to set the handler without using an attribute.
2
Answers
you have declare these variables out of the constructor method
Since you do not want to use configuration attributes, you’ll need to provide the configuration manually.
E.g.:
With the above, any class that implements
CommandHandlerInterface
will be registered as a command handler for Messenger, and be directed to use the "command" bus.You may need to perform some application specific adjustments to your own use-case, but this should take you in the right direction.
Docs: