Spaces:
No application file
No application file
mautic
/
app
/bundles
/IntegrationsBundle
/Sync
/SyncProcess
/Direction
/Internal
/ObjectChangeGenerator.php
declare(strict_types=1); | |
namespace Mautic\IntegrationsBundle\Sync\SyncProcess\Direction\Internal; | |
use Mautic\IntegrationsBundle\Exception\InvalidValueException; | |
use Mautic\IntegrationsBundle\Sync\DAO\Mapping\FieldMappingDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Mapping\MappingManualDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Mapping\ObjectMappingDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\InformationChangeRequestDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Order\FieldDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Order\ObjectChangeDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Report\FieldDAO as ReportFieldDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Report\ObjectDAO as ReportObjectDAO; | |
use Mautic\IntegrationsBundle\Sync\DAO\Sync\Report\ReportDAO; | |
use Mautic\IntegrationsBundle\Sync\Exception\ConflictUnresolvedException; | |
use Mautic\IntegrationsBundle\Sync\Exception\FieldNotFoundException; | |
use Mautic\IntegrationsBundle\Sync\Exception\ObjectNotFoundException; | |
use Mautic\IntegrationsBundle\Sync\Logger\DebugLogger; | |
use Mautic\IntegrationsBundle\Sync\SyncDataExchange\Helper\FieldHelper; | |
use Mautic\IntegrationsBundle\Sync\SyncDataExchange\MauticSyncDataExchange; | |
use Mautic\IntegrationsBundle\Sync\SyncJudge\SyncJudgeInterface; | |
use Mautic\IntegrationsBundle\Sync\SyncProcess\Direction\Helper\ValueHelper; | |
class ObjectChangeGenerator | |
{ | |
private array $judgementModes = [ | |
SyncJudgeInterface::HARD_EVIDENCE_MODE, | |
SyncJudgeInterface::BEST_EVIDENCE_MODE, | |
SyncJudgeInterface::FUZZY_EVIDENCE_MODE, | |
]; | |
public function __construct( | |
private SyncJudgeInterface $syncJudge, | |
private ValueHelper $valueHelper, | |
private FieldHelper $fieldHelper | |
) { | |
} | |
/** | |
* @return ObjectChangeDAO | |
* | |
* @throws ObjectNotFoundException | |
*/ | |
public function getSyncObjectChange( | |
ReportDAO $syncReport, | |
MappingManualDAO $mappingManual, | |
ObjectMappingDAO $objectMapping, | |
ReportObjectDAO $internalObject, | |
ReportObjectDAO $integrationObject | |
) { | |
$objectChange = new ObjectChangeDAO( | |
$mappingManual->getIntegration(), | |
$internalObject->getObject(), | |
$internalObject->getObjectId(), | |
$integrationObject->getObject(), | |
$integrationObject->getObjectId() | |
); | |
if ($internalObject->getObjectId()) { | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
"Integration to Mautic; found a match between Mautic's %s:%s object and the integration %s:%s object ", | |
$internalObject->getObject(), | |
(string) $internalObject->getObjectId(), | |
$integrationObject->getObject(), | |
(string) $integrationObject->getObjectId() | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
} else { | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
'Integration to Mautic; no match found for %s:%s', | |
$integrationObject->getObject(), | |
(string) $integrationObject->getObjectId() | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
} | |
/** @var FieldMappingDAO[] $fieldMappings */ | |
$fieldMappings = $objectMapping->getFieldMappings(); | |
foreach ($fieldMappings as $fieldMappingDAO) { | |
$this->addFieldToObjectChange($fieldMappingDAO, $syncReport, $mappingManual, $internalObject, $integrationObject, $objectChange); | |
} | |
// Set the change date/time from the object so that we can update last sync date based on this | |
$objectChange->setChangeDateTime($integrationObject->getChangeDateTime()); | |
return $objectChange; | |
} | |
/** | |
* @throws ObjectNotFoundException | |
*/ | |
private function addFieldToObjectChange( | |
FieldMappingDAO $fieldMappingDAO, | |
ReportDAO $syncReport, | |
MappingManualDAO $mappingManual, | |
ReportObjectDAO $internalObject, | |
ReportObjectDAO $integrationObject, | |
ObjectChangeDAO $objectChange | |
): void { | |
// Skip adding fields for the pull process that should sync to integration only. | |
if (ObjectMappingDAO::SYNC_TO_INTEGRATION === $fieldMappingDAO->getSyncDirection()) { | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
"Integration to Mautic; the %s object's field %s was skipped because it's configured to sync to the integration", | |
$internalObject->getObject(), | |
$fieldMappingDAO->getInternalField() | |
), | |
__CLASS__.':'.__FUNCTION__ | |
); | |
return; | |
} | |
try { | |
$integrationFieldState = $integrationObject->getField($fieldMappingDAO->getIntegrationField())->getState(); | |
$internalFieldState = $this->getFieldState( | |
$fieldMappingDAO->getInternalObject(), | |
$fieldMappingDAO->getInternalField(), | |
$integrationFieldState | |
); | |
$integrationInformationChangeRequest = $syncReport->getInformationChangeRequest( | |
$integrationObject->getObject(), | |
$integrationObject->getObjectId(), | |
$fieldMappingDAO->getIntegrationField() | |
); | |
} catch (FieldNotFoundException) { | |
return; | |
} | |
// If syncing bidirectional, let the sync judge determine what value should be used for the field | |
if (ObjectMappingDAO::SYNC_BIDIRECTIONALLY === $fieldMappingDAO->getSyncDirection()) { | |
$this->judgeThenAddFieldToObjectChange($mappingManual, $internalObject, $fieldMappingDAO, $integrationInformationChangeRequest, $objectChange, $internalFieldState); | |
return; | |
} | |
try { | |
$newValue = $this->valueHelper->getValueForMautic( | |
$integrationInformationChangeRequest->getNewValue(), | |
$internalFieldState, | |
$fieldMappingDAO->getSyncDirection() | |
); | |
} catch (InvalidValueException) { | |
return; // Field has to be skipped | |
} | |
// Add the value to the field based on the field state | |
$objectChange->addField( | |
new FieldDAO($fieldMappingDAO->getInternalField(), $newValue), | |
$internalFieldState | |
); | |
// ObjectMappingDAO::SYNC_TO_MAUTIC | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
'Integration to Mautic; syncing %s %s with a value of %s', | |
$internalFieldState, | |
$fieldMappingDAO->getInternalField(), | |
var_export($newValue->getNormalizedValue(), true) | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
} | |
private function judgeThenAddFieldToObjectChange( | |
MappingManualDAO $mappingManual, | |
ReportObjectDAO $internalObject, | |
FieldMappingDAO $fieldMappingDAO, | |
InformationChangeRequestDAO $integrationInformationChangeRequest, | |
ObjectChangeDAO $objectChange, | |
string $fieldState | |
): void { | |
try { | |
$internalField = $internalObject->getField($fieldMappingDAO->getInternalField()); | |
} catch (FieldNotFoundException) { | |
$internalField = null; | |
} | |
if (!$internalField) { | |
$newValue = $this->valueHelper->getValueForMautic( | |
$integrationInformationChangeRequest->getNewValue(), | |
$fieldState, | |
$fieldMappingDAO->getSyncDirection() | |
); | |
$objectChange->addField( | |
new FieldDAO($fieldMappingDAO->getInternalField(), $newValue), | |
$fieldState | |
); | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
"Integration to Mautic; the sync is bidirectional but no conflicts were found so syncing the %s object's %s field %s with a value of %s", | |
$internalObject->getObject(), | |
$fieldState, | |
$fieldMappingDAO->getInternalField(), | |
var_export($newValue->getNormalizedValue(), true) | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
return; | |
} | |
$internalInformationChangeRequest = new InformationChangeRequestDAO( | |
MauticSyncDataExchange::NAME, | |
$internalObject->getObject(), | |
$internalObject->getObjectId(), | |
$internalField->getName(), | |
$internalField->getValue() | |
); | |
$possibleChangeDateTime = $internalObject->getChangeDateTime(); | |
$certainChangeDateTime = $internalField->getChangeDateTime(); | |
// If we know certain change datetime and it's newer than possible change datetime | |
// then we have to update possible change datetime otherwise comparision doesn't work correctly | |
if ($certainChangeDateTime && ($certainChangeDateTime > $possibleChangeDateTime)) { | |
$possibleChangeDateTime = $certainChangeDateTime; | |
} | |
$internalInformationChangeRequest->setPossibleChangeDateTime($possibleChangeDateTime); | |
$internalInformationChangeRequest->setCertainChangeDateTime($certainChangeDateTime); | |
// There is a conflict so let the judge determine which value comes out on top | |
foreach ($this->judgementModes as $judgeMode) { | |
try { | |
$this->makeJudgement( | |
$mappingManual, | |
$judgeMode, | |
$fieldMappingDAO, | |
$objectChange, | |
$integrationInformationChangeRequest, | |
$internalInformationChangeRequest, | |
$fieldState | |
); | |
break; | |
} catch (ConflictUnresolvedException) { | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
'Integration to Mautic; no winner was determined using the %s judging mode for object %s field %s', | |
$judgeMode, | |
$internalObject->getObject(), | |
$fieldMappingDAO->getInternalField() | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
} | |
} | |
} | |
/** | |
* @throws ConflictUnresolvedException | |
*/ | |
private function makeJudgement( | |
MappingManualDAO $mappingManual, | |
string $judgeMode, | |
FieldMappingDAO $fieldMappingDAO, | |
ObjectChangeDAO $objectChange, | |
InformationChangeRequestDAO $integrationInformationChangeRequest, | |
InformationChangeRequestDAO $internalInformationChangeRequest, | |
string $fieldState | |
): void { | |
$winningChangeRequest = $this->syncJudge->adjudicate( | |
$judgeMode, | |
$internalInformationChangeRequest, | |
$integrationInformationChangeRequest | |
); | |
$newValue = $this->valueHelper->getValueForMautic( | |
$winningChangeRequest->getNewValue(), | |
$fieldState, | |
$fieldMappingDAO->getSyncDirection() | |
); | |
$objectChange->addField( | |
new FieldDAO($fieldMappingDAO->getInternalField(), $newValue), | |
$fieldState | |
); | |
DebugLogger::log( | |
$mappingManual->getIntegration(), | |
sprintf( | |
"Integration to Mautic; sync judge determined to sync %s to the %s object's %s field %s with a value of %s using the %s judging mode", | |
$winningChangeRequest->getIntegration(), | |
$winningChangeRequest->getObject(), | |
$fieldState, | |
$fieldMappingDAO->getInternalField(), | |
var_export($newValue->getNormalizedValue(), true), | |
$judgeMode | |
), | |
self::class.':'.__FUNCTION__ | |
); | |
} | |
private function getFieldState(string $object, string $field, string $integrationFieldState): string | |
{ | |
// If this is a Mautic required field, return required | |
if (isset($this->fieldHelper->getRequiredFields($object)[$field])) { | |
return ReportFieldDAO::FIELD_REQUIRED; | |
} | |
return $integrationFieldState; | |
} | |
} | |