skip to Main Content

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


  1. you have declare these variables out of the constructor method

    class CreateTodoCommand {
        private string $name,
        private string $text,
        private string $userId
    public function __construct(
        
    ) { }
    
    Login or Signup to reply.
  2. Since you do not want to use configuration attributes, you’ll need to provide the configuration manually.

    E.g.:

    # config/services.yaml
    services:
        # this config only applies to the services created by this file
        _instanceof:
            # services whose classes are instances of CustomInterface will be tagged automatically
            AppTodoApplicationInterfacesCommandHandlerInterface:
                tags: ['messenger.message_handler' { bus: 'command.bus' }]
        # ...
    

    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:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search