selectedPortals(); if ($portals === []) { $this->error('Ungültiges Portal. Erlaubt: presseecho, businessportal24, all.'); return self::FAILURE; } $dryRun = (bool) $this->option('dry-run'); $force = (bool) $this->option('force'); $skipVariants = (bool) $this->option('skip-variants'); $limit = max(0, (int) $this->option('limit')); $totals = [ 'referenced' => 0, 'copied' => 0, 'variants_generated' => 0, 'updated' => 0, 'already_synced' => 0, 'missing' => 0, 'unused' => 0, ]; foreach ($portals as $portal) { $stats = $this->syncPortal($portal, $dryRun, $force, $skipVariants, $limit); foreach ($totals as $key => $value) { $totals[$key] = $value + $stats[$key]; } $this->line(sprintf( '%s: referenziert %d, kopiert %d, Varianten %d, DB-Updates %d, bereits synchron %d, fehlend %d, ungenutzt %d', $portal, $stats['referenced'], $stats['copied'], $stats['variants_generated'], $stats['updated'], $stats['already_synced'], $stats['missing'], $stats['unused'], )); } $this->newLine(); $this->info(sprintf( 'Gesamt: referenziert %d, kopiert %d, Varianten %d, DB-Updates %d, bereits synchron %d, fehlend %d, ungenutzt %d%s', $totals['referenced'], $totals['copied'], $totals['variants_generated'], $totals['updated'], $totals['already_synced'], $totals['missing'], $totals['unused'], $dryRun ? ' (Dry-Run)' : '', )); return $totals['missing'] > 0 ? self::FAILURE : self::SUCCESS; } /** * @return list */ private function selectedPortals(): array { $portal = (string) $this->option('portal'); return match ($portal) { 'all' => [Portal::Presseecho->value, Portal::Businessportal24->value], Portal::Presseecho->value, Portal::Businessportal24->value => [$portal], default => [], }; } /** * @return array{referenced:int,copied:int,variants_generated:int,updated:int,already_synced:int,missing:int,unused:int} */ private function syncPortal(string $portal, bool $dryRun, bool $force, bool $skipVariants, int $limit): array { $sourceDirectory = "{$portal}/press_release"; $allSourceFiles = collect(Storage::disk('public')->files($sourceDirectory)) ->mapWithKeys(fn (string $path): array => [basename($path) => $path]); $referencedFilenames = []; $stats = [ 'referenced' => 0, 'copied' => 0, 'variants_generated' => 0, 'updated' => 0, 'already_synced' => 0, 'missing' => 0, 'unused' => 0, ]; $query = PressReleaseImage::withoutGlobalScopes() ->where('legacy_portal', $portal) ->whereNotNull('path') ->select(['id', 'press_release_id', 'legacy_portal', 'path', 'variants']) ->orderBy('id'); $processed = 0; $reachLimit = false; $query->chunkById(500, function ($images) use ( $allSourceFiles, $dryRun, $force, $skipVariants, $portal, $limit, &$referencedFilenames, &$stats, &$processed, &$reachLimit, ): bool { foreach ($images as $image) { if ($reachLimit) { return false; } $stats['referenced']++; $sourceFilename = basename((string) $image->path); $referencedFilenames[$sourceFilename] = true; if (Str::startsWith((string) $image->path, 'press-releases/')) { $stats['already_synced']++; if (! $skipVariants && (! is_array($image->variants) || $image->variants === [])) { if (! $dryRun) { $variants = $this->imageService->generateMissingPressReleaseVariants($image->path); if ($variants !== []) { $image->forceFill(['variants' => $variants])->save(); $stats['variants_generated']++; $stats['updated']++; } } else { $stats['variants_generated']++; } } $processed++; if ($limit > 0 && $processed >= $limit) { $reachLimit = true; return false; } continue; } $sourcePath = $allSourceFiles->get($sourceFilename); if (! $sourcePath) { $stats['missing']++; $this->warn("Fehlt: {$portal}/press_release/{$sourceFilename} (Image #{$image->id})"); continue; } $destinationPath = "press-releases/{$image->press_release_id}/images/{$sourceFilename}"; if (! $dryRun) { if ($force || ! Storage::disk('public')->exists($destinationPath)) { Storage::disk('public')->copy($sourcePath, $destinationPath); $stats['copied']++; } } else { $stats['copied']++; } $variants = []; if (! $skipVariants && ! $dryRun) { $variants = $this->imageService->generateMissingPressReleaseVariants($destinationPath); if ($variants !== []) { $stats['variants_generated']++; } } elseif (! $skipVariants && $dryRun) { $stats['variants_generated']++; } if (! $dryRun) { $size = @getimagesize(Storage::disk('public')->path($destinationPath)) ?: [null, null]; $image->forceFill([ 'disk' => 'public', 'path' => $destinationPath, 'variants' => $variants !== [] ? $variants : $image->variants, 'width' => is_int($size[0] ?? null) ? $size[0] : $image->width, 'height' => is_int($size[1] ?? null) ? $size[1] : $image->height, ])->save(); } $stats['updated']++; $processed++; if ($limit > 0 && $processed >= $limit) { $reachLimit = true; return false; } } return true; }); $stats['unused'] = $allSourceFiles->keys() ->reject(fn (string $filename): bool => isset($referencedFilenames[$filename])) ->count(); return $stats; } }