I am using API Platform 3.1. I need to generate thumbnails on some entities after creation. I want to use Symfony Messenger to trigger this and do it asynchronously since it could take time some to process, but I want the entity to be saved immediately.
When a resource uses both the messenger and a custom processor (to save the entity), either the messages are not created if using messenger=true
, or the processor is not called if using messenger='input'
.
How to reproduce
#[ORMEntity]
#[ApiResource(
operations: [
new Post(
processor: MyEntityProcessor::class,
messenger: 'input',
deserialize: false,
)
]
)]
class MyEntity
{
}
final class MyEntityProcessor implements ProcessorInterface
{
public function __construct(private ProcessorInterface $persistProcessor)
{
}
public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
{
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
return $result;
}
}
final class MyEntityHandler implements MessageHandlerInterface
{
public function __construct(private EntityManagerInterface $entityManager)
{
}
public function __invoke(MyEntity $myEntity)
{
// my long running function
$this->entityManager->persist($myEntity);
$this->entityManager->flush();
}
}
services.yaml
services:
AppStateMyEntityProcessor:
bind:
$persistProcessor: '@api_platform.doctrine.orm.state.item_provider'
messenger.yaml
framework:
messenger:
transports:
async: 'doctrine://default'
routing:
'AppEntityMyEntity': async
API Platform documentation mentions in Symfony Messenger Integration:
Note: when using
messenger=true
ApiResource attribute in a Doctrine entity, the Doctrine Processor is not called. If you want the Doctrine Processor to be called, you should decorate a built-in state processor and implement your own logic.
It was suggested on a GitHub issue that I should decorate the messenger handler in order to save the entity. However, I need my entity to be saved immediately without waiting for it to be consumed by the messenger:consume
worker.
2
Answers
It seems you have encountered a specific challenge with API Platform and Symfony Messenger integration, where you want to use Symfony Messenger to trigger asynchronous thumbnail generation while ensuring immediate entity saving. This can be a bit tricky due to the interactions between the processor, messenger, and entity saving. Here’s a potential approach you could consider:
Decouple Entity Creation and Thumbnail Generation:
Instead of triggering thumbnail generation directly from the entity creation process, consider decoupling it. Upon entity creation, immediately save the entity and use Symfony Messenger to dispatch a message indicating that a thumbnail needs to be generated for the newly created entity.
Use a Message Handler for Thumbnail Generation:
Create a separate message handler that listens for the thumbnail generation messages dispatched by the entity creation process. This handler should be responsible for generating thumbnails and updating the entity with the generated thumbnail information. It can also persist and flush the entity immediately after updating it.
Entity Processing:
In the
MyEntityProcessor
class, focus solely on processing the entity and avoid dealing with the thumbnail generation logic. This way, you can maintain the separation of concerns and keep the logic clean.Here’s a high-level overview of how the components might interact:
MyEntity
instance, theMyEntityProcessor
processes it by saving it immediately.MyEntityHandler
message handler receives the message, generates the thumbnail, updates the entity, and then immediately persists and flushes the entity to the database.By structuring your application in this way, you can achieve the immediate saving of the entity while still utilizing Symfony Messenger for asynchronous thumbnail generation.
Remember to configure your message handlers and routing correctly in
messenger.yaml
to ensure that the thumbnail generation message is handled by the appropriate message handler (MyEntityHandler
).This approach requires some restructuring of your code but should help you achieve your goal of immediate entity saving and asynchronous thumbnail generation using Symfony Messenger and API Platform.
A possible solution would be to dispatch the
Message
in yourProcessor
after you have persisted theEntity
for the first time. Maybe not as clean, since it requires more code, however you have way more control over how and when theMessage
is handled. In addition, you could also dispatch it with aDelayStamp
.In your processor I would dispatch the
Message
like this:Create the
Message
AppMessageGenerateThumbMessage.php
:And then create your
MessageHandler
AppMessageGenerateThumbMessageHandler.php
:Your
messenger.yaml
config should look something like this:In the config, in stead of using the
Entity
, you have configured theMessage
to be processed async.A similary solution can be found in the API Platform documentation