-
Notifications
You must be signed in to change notification settings - Fork 13
/
OrderExecutioner.php
174 lines (144 loc) · 6.97 KB
/
OrderExecutioner.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<?php
declare(strict_types=1);
namespace MauticPlugin\HelloWorldBundle\Sync\DataExchange;
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Order\FieldDAO;
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Order\ObjectChangeDAO;
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Order\OrderDAO;
use MauticPlugin\HelloWorldBundle\Connection\Client;
use MauticPlugin\HelloWorldBundle\Sync\Mapping\Manual\MappingManualFactory;
class OrderExecutioner
{
/**
* @var Client
*/
private $client;
/**
* @var ValueNormalizer
*/
private $valueNormalizer;
/**
* @var OrderDAO
*/
private $order;
/**
* @var array<string,mixed[]>
*/
private $mappedObjects = [
MappingManualFactory::CITIZEN_OBJECT => [],
MappingManualFactory::WORLD_OBJECT => [],
];
public function __construct(Client $client)
{
$this->client = $client;
$this->valueNormalizer = new ValueNormalizer();
}
public function execute(OrderDAO $orderDAO): void
{
$this->order = $orderDAO;
// This integration supports two objects, citizen and world
foreach ([MappingManualFactory::CITIZEN_OBJECT, MappingManualFactory::WORLD_OBJECT] as $objectName) {
// Fetch the list of Mautic objects that have already been mapped to an object in the integration
// and thus needs to be updated in the integration
$identifiedObjects = $orderDAO->getIdentifiedObjects()[$objectName] ?? [];
// Fetch the list of Mautic objects that have not been mapped to an object in the integration and
// thus may need to be created or modified in the integration.
$unidentifiedObjects = $orderDAO->getUnidentifiedObjects()[$objectName] ?? [];
// Some integrations may require handling the two groups in different ways.
// For the purpose of this example, it's assumed that the integration has a native upsert feature
// Could also use $orderDAO->getChangedObjectsByObjectType($objectName) to get the same thing without
// the merge.
$changedObjects = array_merge($identifiedObjects, $unidentifiedObjects);
// Not all integrations can be this easy. Some require more complicated processes such as checking if they
// already exist in the integration before creating/updating and the like.
if (!$changedObjects) {
continue;
}
$this->upsertObjects($objectName, $changedObjects);
}
}
/**
* @param ObjectChangeDAO[] $changedObjects
*/
private function upsertObjects(string $objectName, array $changedObjects): void
{
$data = [];
foreach ($changedObjects as $objectChangeDAO) {
$data[] = $this->prepareFieldPayload($objectChangeDAO);
}
$response = $this->client->upsert($objectName, $data);
$this->processResponse($objectName, $response);
}
/**
* @return array<string,mixed>
*/
private function prepareFieldPayload(ObjectChangeDAO $objectChangeDAO): array
{
if ($id = $objectChangeDAO->getObjectId()) {
// If the object is identified, just updated with the modified data
$fields = $objectChangeDAO->getChangedFields();
$datum = ['id' => $id];
} else {
// Otherwise, merge required and changed fields to ensure a full profile.
// This is simplified for the purposes of this example but may require more complex handling
// for some integrations such as making API calls to determine if they already exist in the
// integration
$fields = array_merge($objectChangeDAO->getRequiredFields(), $objectChangeDAO->getChangedFields());
$datum = [];
}
// For the purpose of this example, it will be assumed that the integration's API accepts an identifier that is returned
// in the response in order to identify which sub-response is associated with the payload.
$datum['metadata'] = ['mautic_id' => $objectChangeDAO->getMappedObjectId()];
// Store the object to the mapped Mautic ID for retrieval when processing the response
$this->mappedObjects[$objectChangeDAO->getObject()][$objectChangeDAO->getMappedObjectId()] = $objectChangeDAO;
/** @var FieldDAO $field */
foreach ($fields as $field) {
// Transform the data format from Mautic to what the integration expects
$datum[$field->getName()] = $this->valueNormalizer->normalizeForIntegration(
$field->getValue()
);
}
return $datum;
}
/**
* @param mixed[] $response
*/
private function processResponse(string $objectName, array $response): void
{
foreach ($response as $itemResponse) {
// Set the Mautic ID passed through back to us through the API to find the associated ObjectChangeDAO
$mauticId = $itemResponse['metadata']['mautic_id'];
$objectChangeDAO = $this->mappedObjects[$objectName][$mauticId];
// The order should be updated with the results of the sync by passing in the ObjectChangeDAO to appropriate method
switch ($itemResponse['code']) {
case 200:
// The object was updated so mark the last sync date
$this->order->updateLastSyncDate($objectChangeDAO);
break;
case 201:
// The object was created so map the integration object to the Mautic object
$this->order->addObjectMapping(
$objectChangeDAO,
$objectChangeDAO->getObject(),
$itemResponse['id']
);
break;
case 400:
// Validation failed or some other transient error. Note that this will not automatically retry the sync later
// unless $this->order->retrySyncLater() is used. See 503.
$this->order->noteObjectSyncIssue($objectChangeDAO, $itemResponse['message']);
break;
case 404:
// It's assumed this means that the object no longer exists in the integration and thus mark it as deleted
// so that Mautic does not continue to attempt syncing.
$this->order->deleteObject($objectChangeDAO);
break;
case 503:
// There was a temporary issue communicating with the server so retry this one again with the next sync.
$this->order->retrySyncLater($objectChangeDAO);
break;
}
// There is also the option to remap an object if for example it was converted from a Lead to a Contact
// $this->order->remapObject($objectChangeDAO, $objectChangeDAO->getObjectId(), 'ANOTHER_OBJECT', $itemResponse['id']);
}
}
}