Hello, I’m developing a plugin for Mautic 5.2 which adds an action to the campaign editor which delays the execution until a later moment. But when I try to reschedule the action by failing the event, Mautic disables the campaign due to multiple failings.
How would I correctly delay the execution of a campaign action in a plugin?
Currently, I’ve got the following code:
public function onExecuteCampaignAction(PendingEvent $event): void
{
foreach ($event->getPending() as $pending) {
$delayUntil = new \DateTime($this->getTheDelayFromAService());
if ($delayUntil > new \DateTime('now')) {
// Schedule for later execution
$event->fail($pending, 'Delay date not reached yet', $delayUntil->diff(new \DateTime('now')));
} else {
// Pass the event
$event->pass($pending);
}
}
}
Searching further, I tried the following code without success (The method passWithError() resets the logs isScheduled and triggerDate fields):
if ($delayUntil > new \DateTime()) {
// If the delay date is in the future, schedule it
$log->setTriggerDate($delayUntil);
$log->setIsScheduled(true);
// Pass with note about scheduling
$event->passWithError(
$log,
'Dynamic delay until ' . $delayUntil->format('Y-m-d H:i:s')
);
} else {
// Pass the event immediately if the date is now or in the past
$event->pass($log);
}
Apparently more than 10% failed event disables a campaign automatically. The code responsible is in the campaign bundle, class CampaignEventSubscriber#onEventFailed.
So failing an event is the wrong way. But passing an event clears the rescheduling. I’m at a loss on how to do this.
But what about the case when in one PendingEvent there are multiple LeadEventLog entries with different delays?
public function onExecuteCampaignAction(PendingEvent $event): void
{
foreach ($event->getPending() as $pending) {
// Get delay for this log, might be a different lead
$delayUntil = new \DateTime($this->getTheDelayFromAServiceDependingOnTheLead($pending));
if ($delayUntil > new \DateTime('now')) {
// Schedule for later execution
$event->fail($pending, 'Delay date not reached yet', $delayUntil->diff(new \DateTime('now')));
} else {
// Pass the event
$event->pass($pending);
}
}
}
I guess I don’t really understand the relationship between LeadEventLog entries and the event itself.
There are 2 event classes here. One is the event used in the dispatcher, the PendingEvent. Just a DTO object containing some information about campaign execution that is happening. It contains a campaign Event which is a Doctrine entity class which is a representation of the campaign_events table record. It has an information about what should be done for the contacts passing through the campaign. And there is also a collection of the LeacEventLog records which are records from the campaign_lead_event_log table that are representation of what should/was done to specific contact passing through the campaign event.
I’m not sure I made it clearer or the other way around…
My assumption was that every contact going through a campaign gets its own event. This is obviously not the case.
I think what I’m trying to do here is not supported by the Mautic architecture and I will have to do a workaround.
(For context: I am trying to design a campaign which gets immediately triggered by orders in a ticket shop, but should send out a reminder mail only the day before the ticket is valid. That’s why I was writing a dynamic delay action which was supposed to wait until a date specified in a form field.)
Create a segment “Created Order” where you add contacts that make an order. You didn’t mention how that happen but I guess some custom field is set so you can filter for it.
Create a campaign with the source of the “Created order” segment that will send the email with the ticket confirmation.
Create a segment “Ticket Valid Reminder” which will filter for contacts that have the “event valid date” custom field set for “today”
Create a campaign that will use the “Ticket Valid Reminder” contacts and send the reminder email.
Thank you very much. I think I’ll go with your segments solution instead of further fiddling with events.
My contacts are created with a custom integrations plugin. Ticket orders are currently simulated form submissions so I can attach them to a contact. I’ll look into the custom objects plugin for storing the orders, that sounds promising as well.