Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Messenger component does not work with PUT, DELETE, GET #3082

Closed
mxkh opened this issue Sep 21, 2019 · 7 comments
Closed

Messenger component does not work with PUT, DELETE, GET #3082

mxkh opened this issue Sep 21, 2019 · 7 comments

Comments

@mxkh
Copy link

mxkh commented Sep 21, 2019

Hi! I want to use Message component with PUT\PATCH, DELETE and GET but as I understand the message component work only with POST method, am I right? For example

/**
 * @ApiResource(
 *     shortName="Club",
 *     routePrefix="crm",
 *     messenger="input",
 *     collectionOperations={
 *          "get",
 *          "post"={"path"="/clubs", "input"=CreateClubCommand::class},
 *     },
 *     itemOperations={
 *          "get"={"path"="/clubs/{id}", "input"=FindClubByNameQueryItem::class},
 *          "put"={"path"="/clubs/{id}", "input"=UpdateClubCommand::class}
 *     },
 *     normalizationContext={"groups"={"club_item", "club_list"}},
 *     denormalizationContext={"groups"={"club_write"}}
 * )
 */
final class ClubResource

In this case, I expect that all input DTO's (commands\queries) will be passed to their handlers as in POST case. Is it possible to do without custom code? How do you see it's better to use message component for other request methods e.g. GET, PUT, DELETE to implement CQS or CQRS?

@soyuka
Copy link
Member

soyuka commented Sep 23, 2019

The fact is that input data is given by the user when creating the request, because "read" routes are not denormalizing data they won't work with input classes, indeed they're queries. I don't really understand the need to use messenger for read routes, or anything that does not need asynchronous processing.

@mxkh
Copy link
Author

mxkh commented Sep 23, 2019

@soyuka Hi! Thank you for your response.
I tested today POST and PUT again and noticed that PUT works well with message component, I guess previously it was a cache problem and in my case PUT Command did not pass to the handler.

I agree with you at some point about GET operations, but what about DELETE. DELETE is a mutator and in my opinion, should work in the same way as other mutators (POST or PUT).

@dunglas dunglas changed the title Message component does not work with PUT, DELETE, GET Messenger component does not work with PUT, DELETE, GET Sep 26, 2019
@dunglas
Copy link
Member

dunglas commented Sep 26, 2019

DELETE is already supported. However, it would be great to be able to detect in the handler what was the original HTTP verb.
My suggestion is to create a new ContextStamp containing the content of the $context parameter of DataPersister::persist() and DataPersister::remove(). As already done for remove: https://github.com/api-platform/core/blob/master/src/Bridge/Symfony/Messenger/DataPersister.php#L94

It would allow to access the whole context (including the HTTP verb used), and I'm sure that it will be useful to many!

sergepavle added a commit to sergepavle/core that referenced this issue Oct 6, 2019
sergepavle added a commit to sergepavle/core that referenced this issue Oct 6, 2019
dunglas pushed a commit that referenced this issue Oct 14, 2019
* Issue #3082: Add and use ContextStamp.

* Issue #3082: Add tests.

* Issue #3157: Correct passing of context.

* Issue #3157: Minor corrections.
@soyuka
Copy link
Member

soyuka commented Oct 18, 2019

Isn't yet fixed see #3157 (comment)

warslett pushed a commit to BiffBangPow/core that referenced this issue Oct 18, 2019
* Issue api-platform#3082: Add and use ContextStamp.

* Issue api-platform#3082: Add tests.

* Issue api-platform#3157: Correct passing of context.

* Issue api-platform#3157: Minor corrections.
norkunas pushed a commit to norkunas/core that referenced this issue Dec 2, 2019
* Issue api-platform#3082: Add and use ContextStamp.

* Issue api-platform#3082: Add tests.

* Issue api-platform#3157: Correct passing of context.

* Issue api-platform#3157: Minor corrections.
@toby-griffiths
Copy link
Contributor

Is this still an issue. I'm likely to want to use this use case imminently in my project. Happy to work on a PR if need be, if someone can advise on what is still outstanding on this. The comments on #3157 (comment) don't make it clear what's still required on this and it's over a year old, so I guess a lot has changed since this was last discussed.

@soyuka soyuka closed this as completed Dec 2, 2020
@entermix
Copy link

entermix commented Jan 12, 2021

Hello everyone!

Symfony Messenger still doesn't support the ability to implement what the author wanted? I have a similar problem:

Symfony 5.2 with api-platform 2.5:

// src/Entity/User.php

 * @ApiResource(
 *     messenger="input",
 *     collectionOperations={
 *         "get",
 *         "post"={"status"=202, "input"=CreateUserCommand::class, "output"=false}
 *     },
 *     itemOperations={
 *         "put"={"status"=202, "input"=UpdateUserCommand::class, "output"=false},
 *         "delete"={"status"=202, "input"=DeleteUserCommand::class, "output"=false}
 *     }
 * )
 */
class User implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="ulid", unique=true)
     */
    private $id;
    
    /**
     * @ORM\Column(type="string", length=180, unique=true)
     */
    private $email;
    
    public function getId(): ?Ulid
    {
        return $this->id;
    }

    public function setId(Ulid $id): self
    {
        $this->id = $id;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }
}

// src/MessageHandler/CommandHandlerInterface.php

interface CommandHandlerInterface
{
}

// config.servises.yaml

...
    _instanceof:
        # all services implementing the CommandHandlerInterface
        # will be registered on the command.bus bus
        App\MessageHandler\CommandHandlerInterface:
            tags:
                - { name: messenger.message_handler, bus: command.bus }

// src/Message/Command/CreateUserCommand.php

final class CreateUserCommand
{
    /**
     * @Assert\NotBlank
     * @Assert\Ulid(
     *     message = "The id {{ value }} is not a valid Ulid."
     * )
     */
    private $id;

    /**
     * @Assert\NotBlank
     * @Assert\Email(
     *     message = "The email {{ value }} is not a valid email."
     * )
     */
    private $email;
}

// src/MessageHandler/Command/CreateUserCommand.php

final class CreateUserCommandHandler implements CommandHandlerInterface
{
    private $entityManager;

    public function __construct(
        EntityManagerInterface $entityManager,
    )
    {
        $this->entityManager = $entityManager;
    }

    public function __invoke(CreateUserCommand $createUserCommand)
    {
        $user = new User();

        /* @var $id Ulid */
        $id = Ulid::fromString($createUserCommand->getId());

        $user->setId($id);
        $user->setEmail($createUserCommand->getEmail());

        $this->entityManager->persist($user);
        $this->entityManager->flush();
    }
}

CreateUserCommand - OK

I want to implement an update:

// src/Message/Command/UpdateUserCommand.php

final class UpdateUserCommand
{
    /**
     * @Assert\NotBlank
     * @Assert\Email(
     *     message = "The email {{ value }} is not a valid email."
     * )
     */
    private $email;

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): string
    {
        return $this->email = $email;
    }
}

// src/MessageHandler/Command/UpdateUserCommand.php

final class UpdateUserCommandHandler implements CommandHandlerInterface
{
    private $entityManager;
    private $userRepository;

    public function __construct(
        EntityManagerInterface $entityManager,
        UserRepository $userRepository,
    )
    {
        $this->entityManager = $entityManager;
        $this->userRepository = $userRepository;
    }

    public function __invoke(UpdateUserCommand $updateUserCommand)
    {
        // TODO:: How can I get the ID of the resource that is passed in the URL?
    }
}

Unfortunately, I could not find a solution to this problem in open sources. Perhaps someone faced a similar task?

@mxkh, did you manage to find answers to your questions?

Thanks!

@KDederichs
Copy link
Contributor

I know this is quite old and I'll make a new issue regarding that but @entermix in case you (or someone else) are still looking:
Seems like you can inject the RequestStack into the handler and then $this->requestStack->getCurrentRequest()->attributes->get('previous_data') will contain the actual object fetched from the URI....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants