0

Hi I have applications that is dispatching commands(messages) to Amazon SQS queues. I have working environment and my commands are in the queue, but in the wrong format like this:

{ "Messages": [ { "MessageId": "f3373e0d-b47e-2a46-51d8-38dc1ee8f07e", "ReceiptHandle": "dzpfgjsnrkwzmzzvzvddepxfpmxwipviybehoedxeolvbndunmceerozsanhwennjvgeunbsfxfxrrywrlkpeuifcnggnslzfgpkilnrwnlyegkcmlaowvcikxhsisujptanjrboedurezuzihngpfmsyxxzmtqghqyrfyycthxuamqnxblprvbbo", "MD5OfBody": "6a69c6c1e53718f891a409e5a9ba98a4", "Body": "O:36:\"Symfony\\Component\\Messenger\\Envelope\":2:{s:44:\"\0Symfony\\Component\\Messenger\\Envelope\0stamps\";a:1:{s:46:\"Symfony\\Component\\Messenger\\Stamp\\BusNameStamp\";a:1:{i:0;O:46:\"Symfony\\Component\\Messenger\\Stamp\\BusNameStamp\":1:{s:55:\"\0Symfony\\Component\\Messenger\\Stamp\\BusNameStamp\0busName\";s:21:\"messenger.bus.default\";}}}s:45:\"\0Symfony\\Component\\Messenger\\Envelope\0message\";O:29:\"App\\Message\\CreatePostCommand\":1:{s:17:\"\0*\0postDataObject\";O:29:\"App\\DataObject\\PostDataObject\":5:{s:5:\"title\";s:11:\"Lorem ipsum\";s:7:\"excerpt\";s:27:\"Lorem ipsum dolor sit amet.\";s:7:\"content\";s:495:\"Donec laoreet maximus lacus, nec lacinia risus rutrum vitae. Morbi tortor sapien, ornare in congue ac, aliquam quis felis. Pellentesque venenatis, elit eget vehicula viverra, elit lacus congue ante, et accumsan est arcu vitae purus. Aliquam erat volutpat. Nullam rhoncus risus augue, ut rhoncus leo sollicitudin sed. Maecenas fringilla, metus pretium tristique sodales, sem ante condimentum massa, a facilisis ligula lacus quis purus. Vestibulum pharetra rutrum velit, in rutrum mi mollis vitae.\";s:6:\"author\";s:14:\"Bartosz Belski\";s:12:\"published_at\";N;}}}", "Attributes": { "SenderId": "AIDAIT2UOQQY3AUEKVGXU", "SentTimestamp": "1649947456026" } } ] }

I suspect that this is fault of serialization. I want to store it as a json inside the queue. But I have no idea how to accomplish this.

My Command(message):

final class CreatePostCommand
{
    protected PostDataObject $postDataObject;

    public function __construct(PostDataObject $postDataObject)
    {
        $this->postDataObject = $postDataObject;
    }

    public function getPostDataObject(): PostDataObject
    {
        return $this->postDataObject;
    }

    public function setPostDataObject(PostDataObject $postDataObject): self
    {
        $this->postDataObject = $postDataObject;

        return $this;
    }
}

My PostDataObject:

class PostDataObject extends DataObject
{
    public $title;

    public $excerpt;

    public $content;

    public $author;

    public $published_at;
}
Eddy
  • 593
  • 8
  • 22

2 Answers2

1

There are two methods to implement __serialize and __unserialize. Using them convert to/from json in format that you want:

final class CreatePostCommand
{
    protected PostDataObject $postDataObject;

    public function __serialize(): array
    {
        return ['title' => $this->postDataObject->title, 'excerpt' => $this->postDataObject->excerpt, ...];
    }

    public function __deserialize(array $data): void
    {
        $this->postDataObject->title = $data['title'];
        $this->postDataObject->excerpt = $data['excerpt'];
        ...
    }
}
Justinas
  • 41,402
  • 5
  • 66
  • 96
  • It maybe could work, but my `PostDataObject` extending the `DataObject` class that has a constructor like this: `public function __construct(array $parameters)` and I am getting `Could not decode message: Cannot create an instance of "App\DataObject\PostDataObject" from seri alized data because its constructor requires parameter "parameters" to be present.` – Eddy Apr 15 '22 at 09:03
  • @Eddy Try move it to setter: `setPostDataObject(PostDataObject $postDataObject)` – Justinas Apr 15 '22 at 09:10
  • @Justinsas I ended up with custom serializer like described here: https://symfonycasts.com/screencast/messenger/transport-serializer but I still think your idea of using deafult `__serialize/__unserialize` methods can work as well. Do you think I should describe how I solve my problem by using custom serializer in symfony messenger? – Eddy Apr 15 '22 at 11:53
  • @Eddy Yes, you should – Justinas Apr 15 '22 at 12:10
1

my solution was a bit different than what @Justinas proposed. I created my own serializer for symfony messenger like in this tutorial:

My code:

namespace App\Message\Serializers;

use App\DataObject\PostDataObject;
use App\Message\CreatePostCommand;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;

class JsonDataObjectCommandSerializer implements SerializerInterface
{
    private \Symfony\Component\Serializer\SerializerInterface $serializer;

    public function __construct(\Symfony\Component\Serializer\SerializerInterface $serializer)
    {
        $this->serializer = $serializer;
    }

    public function decode(array $encodedEnvelope): Envelope
    {
        $body = $encodedEnvelope['body'];
        $headers = $encodedEnvelope['headers'];
        $data = json_decode($body, true);
        $message = new CreatePostCommand(new PostDataObject($data));
        $stamps = [];
        if (isset($headers['stamps'])) {
            $stamps = json_decode($headers['stamps']);
        }
        dump($data, $message, $stamps);
        return new Envelope($message, $stamps ?? []);
    }

    public function encode(Envelope $envelope): array
    {
        $message = $envelope->getMessage();
        if ($message instanceof CreatePostCommand) {
            $data = $message->getPostDataObject()->toArray();
        } else {
            throw new \Exception('Unsupported message class');
        }
        $allStamps = [];
        foreach ($envelope->all() as $stamps) {
            $allStamps = array_merge($allStamps, $stamps);
        }

        return [
            'body' => json_encode($data),
            'headers' => [
                'stamps' => $this->serializer->serialize($allStamps, 'json')
            ]
        ];
    }
}
Eddy
  • 593
  • 8
  • 22