$backupItemIds */ public function __construct( public int $backupSetId, public array $backupItemIds, public int $initiatorUserId, ?OperationRun $operationRun = null, ) { $this->operationRun = $operationRun; } /** * @return array */ public function middleware(): array { return [new TrackOperationRun]; } public function handle( AuditLogger $auditLogger, ): void { $backupSet = BackupSet::query()->with(['tenant'])->find($this->backupSetId); if (! $backupSet instanceof BackupSet) { if ($this->operationRun) { /** @var OperationRunService $opService */ $opService = app(OperationRunService::class); $opService->updateRun( $this->operationRun, 'completed', 'failed', ['failed' => 1], [['code' => 'backup_set.not_found', 'message' => 'Backup set not found.']] ); } return; } $tenant = $backupSet->tenant; $initiator = User::query()->find($this->initiatorUserId); $requestedIds = collect($this->backupItemIds) ->map(fn (mixed $value): int => (int) $value) ->filter(fn (int $value): bool => $value > 0) ->unique() ->sort() ->values() ->all(); $requestedCount = count($requestedIds); $failures = []; try { /** @var \Illuminate\Database\Eloquent\Collection $items */ $items = BackupItem::query() ->where('backup_set_id', $backupSet->getKey()) ->whereIn('id', $requestedIds) ->get(); $foundIds = $items->pluck('id')->map(fn (mixed $value): int => (int) $value)->all(); $missingIds = array_values(array_diff($requestedIds, $foundIds)); foreach ($missingIds as $missingId) { $failures[] = [ 'code' => 'backup_item.not_found', 'message' => RunFailureSanitizer::sanitizeMessage("Backup item {$missingId} not found (already removed?)."), ]; } $removed = 0; $policyIds = []; $policyIdentifiers = []; foreach ($items as $item) { $item->delete(); $removed++; if ($item->policy_id) { $policyIds[] = (int) $item->policy_id; } if ($item->policy_identifier) { $policyIdentifiers[] = (string) $item->policy_identifier; } } $backupSet->update([ 'item_count' => $backupSet->items()->count(), ]); if ($tenant instanceof Tenant) { $auditLogger->log( tenant: $tenant, action: 'backup.items_removed', resourceType: 'backup_set', resourceId: (string) $backupSet->getKey(), status: 'success', context: [ 'metadata' => [ 'removed_count' => $removed, 'requested_count' => $requestedCount, 'missing_count' => count($missingIds), 'policy_ids' => array_values(array_unique($policyIds)), 'policy_identifiers' => array_values(array_unique($policyIdentifiers)), 'backup_set_id' => (int) $backupSet->getKey(), 'initiator_user_id' => $initiator?->getKey(), ], ], actorId: $initiator?->getKey(), ); } if ($this->operationRun) { /** @var OperationRunService $opService */ $opService = app(OperationRunService::class); $this->operationRun->update([ 'context' => array_merge($this->operationRun->context ?? [], [ 'backup_set_id' => (int) $backupSet->getKey(), 'requested_count' => $requestedCount, 'removed_count' => $removed, 'missing_count' => count($missingIds), 'remaining_count' => (int) $backupSet->item_count, ]), ]); $outcome = 'succeeded'; if ($removed === 0) { $outcome = 'failed'; } elseif ($failures !== []) { $outcome = 'partially_succeeded'; } $opService->updateRun( $this->operationRun, 'completed', $outcome, [ 'total' => $requestedCount, 'processed' => $requestedCount, 'succeeded' => $removed, 'failed' => count($missingIds), 'deleted' => $removed, 'items' => $requestedCount, ], $failures, ); } } catch (Throwable $throwable) { if ($tenant instanceof Tenant) { $auditLogger->log( tenant: $tenant, action: 'backup.items_removed', resourceType: 'backup_set', resourceId: (string) $backupSet->getKey(), status: 'failed', context: [ 'metadata' => [ 'requested_count' => $requestedCount, 'backup_set_id' => (int) $backupSet->getKey(), ], ], actorId: $initiator?->getKey(), ); } if ($this->operationRun) { /** @var OperationRunService $opService */ $opService = app(OperationRunService::class); $opService->failRun($this->operationRun, $throwable); } throw $throwable; } } }