Files
2025-08-02 16:30:27 +02:00

230 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
namespace External\FlexiBeeBundle\Synchronizers;
use External\FlexiBeeBundle\Exception\FlexiBeeException;
use External\FlexiBeeBundle\Util\FlexiBeeApi;
use External\FlexiBeeBundle\Util\FlexiBeeConfiguration;
use External\FlexiBeeBundle\Util\FlexiBeeLogger;
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
abstract class BaseSynchronizer implements SynchronizerInterface
{
public const MODE_NORMAL = 0;
public const MODE_FULL = 1;
protected static string $type;
protected static ?string $evidenceClass = null;
protected bool $logging = true;
protected int $mode = self::MODE_NORMAL;
protected FlexiBeeLogger $logger;
protected FlexiBeeConfiguration $configuration;
protected FlexiBeeApi $flexiBeeApi;
protected FlexiBeeUtil $flexiBeeUtil;
protected ?int $tmpLastSync = null;
public function __construct(
FlexiBeeConfiguration $configuration,
FlexiBeeApi $flexiBeeApi,
FlexiBeeUtil $flexiBeeUtil,
FlexiBeeLogger $logger,
) {
$this->configuration = $configuration;
$this->flexiBeeApi = $flexiBeeApi;
$this->flexiBeeUtil = $flexiBeeUtil;
$this->logger = $logger;
}
public static function getType(): string
{
return static::$type;
}
public function sync(): void
{
$this->process(
$this->getLastSyncTime()
);
$this->updateLastSyncTime();
}
public function syncSingleItem(int $id, ?int $flexiId = null): void
{
throw new FlexiBeeException(sprintf('Single item synchronization is not supported for type "%s"', static::getType()));
}
protected function process(?int $lastSyncTime = null): void
{
foreach ($this->getItems($lastSyncTime) as $item) {
$this->logItem((array) $item);
$this->processItem($item);
}
}
abstract protected function processItem(array $item): void;
protected function getItems(?int $lastSyncTime = null): iterable
{
if (!static::$evidenceClass) {
throw new FlexiBeeException(
sprintf('Class "%s" should overwrite "%s" method', get_class($this), __FUNCTION__)
);
}
// Pokud je nastavena evidence class, tak pro nacteni zmen pouziju ChangesAPI
$changesMaxVersion = $this->getLastVersion();
// ukladam si startTime, abych mohl limitovat maximalni dobu, kterou muze sync v ramci jednoho sync cyklu bezet
$startTime = microtime(true);
do {
// nactu si zmeny od posledni synchronizace
$changes = $this->flexiBeeApi->getChanges($changesMaxVersion, static::$evidenceClass);
// ulozim si maximalni verzi
$changesMaxVersion = (int) max($changesMaxVersion, !empty($changes) ? (max(array_map(fn ($x) => $x['@in-version'], $changes)) + 1) : 0);
// nactu si data podle ID zmen
$changesIds = array_unique(array_map(fn ($x) => $x['id'], $changes));
if (!empty($changesIds)) {
// zavolam preprocess kvuli pripadnym modifikacim dat
$changedItems = $this->preprocessChangedItems(
$this->flexiBeeApi->getEvidenceDataByIds(static::$evidenceClass, $changesIds, filters: $this->getItemsFilter())
);
// zacnu vracet polozky, aby se v synchronizaci zpracovali
foreach ($changedItems as $item) {
yield $item;
}
}
// aktualizuju si posledni sesynchronizovanou verzi
$this->updateLastVersion($changesMaxVersion);
$isTimedOut = (microtime(true) - $startTime) > 300;
// pokud sync bezi uz dele jak 5 minut, tak ji ukoncim
// pravdepodobne ve Flexi vzniklo hodne zmen, tak to postupne syncneme nez blokovat sync na dlouhou dobu
if ($this->mode === self::MODE_NORMAL && $isTimedOut) {
break;
}
} while (!empty($changes));
}
protected function preprocessChangedItems(array $items): array
{
return $items;
}
protected function logItem(array $item): void
{
if (!$this->logging) {
return;
}
$this->logger->data(
sprintf('[FlexiBee] Processing change of \'%s\'', static::getType()),
[
'Data' => $item,
'Type' => static::getType(),
]
);
}
protected function logUpdate(array $item): void
{
if (!$this->logging) {
return;
}
$this->logger->data(
sprintf('[FlexiBee] Sending to Flexi change of \'%s\'', static::getType()),
[
'Data' => $item,
'Type' => static::getType(),
]
);
}
protected function getLastVersion(?string $type = null): int
{
if ($this->mode === self::MODE_FULL) {
return 0;
}
$dbcfg = \Settings::getDefault();
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
return !empty($flexiBee['sync_versions'][$type ?: static::getType()]) ? (int) $flexiBee['sync_versions'][$type ?: static::getType()] : 0;
}
protected function updateLastVersion(int $version): void
{
if ($this->mode === self::MODE_FULL) {
return;
}
$dbcfg = \Settings::getDefault();
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
$flexiBee['sync_versions'][static::getType()] = $version;
$dbcfg->saveValue('flexibee', $flexiBee);
}
protected function getLastSyncTime(?string $type = null): ?int
{
if ($this->mode === self::MODE_FULL) {
return null;
}
$dbcfg = \Settings::getDefault();
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
$this->tmpLastSync = time() - (60 * 5);
return $flexiBee['timestamps'][$type ?: static::getType()] ?? null;
}
protected function updateLastSyncTime(): void
{
if ($this->mode === self::MODE_FULL) {
return;
}
$dbcfg = \Settings::getDefault();
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
$flexiBee['timestamps'][static::getType()] = $this->tmpLastSync ?: (time() - (60 * 5));
$dbcfg->saveValue('flexibee', $flexiBee);
}
protected function createDateTime(?int $timestamp): ?\DateTime
{
if (!$timestamp) {
return null;
}
$datetime = new \DateTime();
$datetime->setTimestamp($timestamp);
return $datetime;
}
public function setMode(int $mode): self
{
$this->mode = $mode;
return $this;
}
protected function getItemsFilter(): array
{
return [];
}
}