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; | |
| } | |
| } | |