mirror of
https://github.com/ceratic/MediaCollectorLibary.git
synced 2026-05-13 23:56:46 +02:00
213 lines
6.4 KiB
PHP
213 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\SyncLog;
|
|
use Exception;
|
|
|
|
abstract class BaseSyncService
|
|
{
|
|
protected \PDO $pdo;
|
|
protected array $source;
|
|
protected SyncLog $syncLog;
|
|
protected int $sourceId;
|
|
|
|
protected $logFileHandle;
|
|
protected $logFilePath;
|
|
|
|
public function __construct(\PDO $pdo, array $source)
|
|
{
|
|
$this->pdo = $pdo;
|
|
$this->source = $source;
|
|
|
|
if (!isset($source['id']) || empty($source['id'])) {
|
|
throw new \Exception('Source ID is required for sync service');
|
|
}
|
|
|
|
$this->sourceId = (int) $source['id'];
|
|
|
|
// Create log file for this sync operation
|
|
$this->initializeLogFile();
|
|
}
|
|
|
|
private function initializeLogFile(): void
|
|
{
|
|
$timestamp = date('Y-m-d_H-i-s');
|
|
$sourceName = strtolower($this->source['name'] ?? 'unknown');
|
|
$this->logFilePath = "logs/{$sourceName}_sync_{$timestamp}.log";
|
|
|
|
// Create logs directory if it doesn't exist
|
|
$logDir = dirname($this->logFilePath);
|
|
if (!is_dir($logDir)) {
|
|
mkdir($logDir, 0755, true);
|
|
}
|
|
|
|
$this->logFileHandle = fopen($this->logFilePath, 'w');
|
|
if ($this->logFileHandle) {
|
|
$this->logProgress("=== Starting {$sourceName} sync at " . date('Y-m-d H:i:s') . " ===");
|
|
}
|
|
}
|
|
|
|
public function __destruct()
|
|
{
|
|
if ($this->logFileHandle) {
|
|
$this->logProgress("=== Sync completed at " . date('Y-m-d H:i:s') . " ===");
|
|
fclose($this->logFileHandle);
|
|
}
|
|
}
|
|
|
|
public function startSync(string $syncType = 'full'): int
|
|
{
|
|
// Set higher limits for long-running syncs
|
|
ini_set('max_execution_time', 3600); // 1 hour
|
|
ini_set('memory_limit', '512M');
|
|
|
|
// Create sync log entry
|
|
$syncLogId = $this->createSyncLog($syncType, 'started');
|
|
$this->currentSyncLogId = $syncLogId;
|
|
|
|
try {
|
|
$this->logProgress("Starting {$syncType} sync for source: " . ($this->source['display_name'] ?? $this->source['name']));
|
|
|
|
$this->executeSync($syncType);
|
|
|
|
// Update sync log as completed
|
|
$this->updateSyncLog($syncLogId, 'completed', [
|
|
'processed_items' => $this->getProcessedCount(),
|
|
'new_items' => $this->getNewCount(),
|
|
'updated_items' => $this->getUpdatedCount(),
|
|
'deleted_items' => $this->getDeletedCount(),
|
|
'message' => "Successfully completed sync"
|
|
]);
|
|
|
|
$this->logProgress("Sync completed successfully");
|
|
|
|
} catch (Exception $e) {
|
|
// Log the full error details
|
|
$errorMessage = $e->getMessage();
|
|
$errorFile = $e->getFile();
|
|
$errorLine = $e->getLine();
|
|
$errorTrace = $e->getTraceAsString();
|
|
|
|
$this->logProgress("CRITICAL ERROR - Sync failed: {$errorMessage}");
|
|
$this->logProgress("Error location: {$errorFile}:{$errorLine}");
|
|
$this->logProgress("Stack trace: {$errorTrace}");
|
|
|
|
// Update sync log as failed with full error details
|
|
$this->updateSyncLog($syncLogId, 'failed', [
|
|
'message' => $errorMessage,
|
|
'errors' => [
|
|
$errorMessage,
|
|
"File: {$errorFile}:{$errorLine}",
|
|
"Stack: " . substr($errorTrace, 0, 1000) // Limit trace size
|
|
]
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
|
|
return $syncLogId;
|
|
}
|
|
|
|
private function createSyncLog(string $syncType, string $status): int
|
|
{
|
|
$data = [
|
|
'source_id' => $this->sourceId,
|
|
'sync_type' => $syncType,
|
|
'status' => $status,
|
|
'total_items' => 0,
|
|
'processed_items' => 0,
|
|
'new_items' => 0,
|
|
'updated_items' => 0,
|
|
'deleted_items' => 0,
|
|
'started_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
$columns = array_keys($data);
|
|
$placeholders = array_map(fn($col) => ":$col", $columns);
|
|
$sql = "INSERT INTO sync_logs (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";
|
|
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->execute($data);
|
|
|
|
return (int) $this->pdo->lastInsertId();
|
|
}
|
|
|
|
protected function updateSyncLog(int $syncLogId, string $status, array $stats = []): bool
|
|
{
|
|
$data = [
|
|
'status' => $status,
|
|
'processed_items' => $stats['processed_items'] ?? 0,
|
|
'new_items' => $stats['new_items'] ?? 0,
|
|
'updated_items' => $stats['updated_items'] ?? 0,
|
|
'deleted_items' => $stats['deleted_items'] ?? 0,
|
|
'completed_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
if (!empty($stats['errors'])) {
|
|
$data['errors'] = json_encode($stats['errors']);
|
|
}
|
|
|
|
if (!empty($stats['message'])) {
|
|
$data['message'] = $stats['message'];
|
|
}
|
|
|
|
$setClause = array_map(fn($col) => "$col = :$col", array_keys($data));
|
|
$sql = "UPDATE sync_logs SET " . implode(', ', $setClause) . " WHERE id = :id";
|
|
$data['id'] = $syncLogId;
|
|
|
|
$stmt = $this->pdo->prepare($sql);
|
|
return $stmt->execute($data);
|
|
}
|
|
|
|
abstract protected function executeSync(string $syncType): void;
|
|
|
|
protected function getProcessedCount(): int
|
|
{
|
|
return 0; // Override in subclasses
|
|
}
|
|
|
|
protected function getNewCount(): int
|
|
{
|
|
return 0; // Override in subclasses
|
|
}
|
|
|
|
protected function getUpdatedCount(): int
|
|
{
|
|
return 0; // Override in subclasses
|
|
}
|
|
|
|
protected function getDeletedCount(): int
|
|
{
|
|
return 0; // Override in subclasses
|
|
}
|
|
|
|
protected $currentSyncLogId = null;
|
|
|
|
protected function logProgress(string $message): void
|
|
{
|
|
$timestamp = date('H:i:s');
|
|
$logMessage = "[{$timestamp}] {$message}\n";
|
|
|
|
// Write to log file if available
|
|
if ($this->logFileHandle) {
|
|
fwrite($this->logFileHandle, $logMessage);
|
|
}
|
|
|
|
// Also write to error log for immediate visibility
|
|
error_log($message);
|
|
|
|
// Update sync log with progress message if we have a current sync log
|
|
if ($this->currentSyncLogId) {
|
|
$this->updateSyncLog($this->currentSyncLogId, 'running', [
|
|
'message' => $message
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function getLogFilePath(): string
|
|
{
|
|
return $this->logFilePath ?? '';
|
|
}
|
|
}
|