From 2ed54f037c5e47d1e49d1b811f0c58a4a6f8ddf7 Mon Sep 17 00:00:00 2001 From: Jack McDade Date: Fri, 13 Feb 2026 23:15:45 -0500 Subject: [PATCH 1/8] Clean Asset Meta Command --- src/Console/Commands/AssetsMetaClean.php | 144 ++++++++++++++++++ src/Providers/ConsoleServiceProvider.php | 1 + .../Console/Commands/AssetsMetaCleanTest.php | 141 +++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 src/Console/Commands/AssetsMetaClean.php create mode 100644 tests/Console/Commands/AssetsMetaCleanTest.php diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php new file mode 100644 index 00000000000..748311cbe6c --- /dev/null +++ b/src/Console/Commands/AssetsMetaClean.php @@ -0,0 +1,144 @@ +getContainers() + ->mapWithKeys(fn ($container) => [$container->handle() => $this->orphanedMetaFiles($container)]); + + $totalOrphanedMetaFiles = $orphanedMetaFilesByContainer->sum->count(); + + if ($totalOrphanedMetaFiles === 0) { + $this->components->info(__('No orphaned metadata files were found.')); + + return self::SUCCESS; + } + + $flatOrphanedMetaFiles = $orphanedMetaFilesByContainer + ->flatMap(fn ($paths, $containerHandle) => $paths->map(fn ($path) => [ + 'container' => $containerHandle, + 'path' => $path, + ])) + ->values(); + + if ($this->option('dry-run')) { + $this->components->warn(__('Found :count orphaned metadata :files.', [ + 'count' => $totalOrphanedMetaFiles, + 'files' => Str::plural('file', $totalOrphanedMetaFiles), + ])); + + $flatOrphanedMetaFiles->each(function ($metaFile) { + $this->line("[{$metaFile['container']}] {$metaFile['path']}"); + }); + + return 0; + } + + progress( + label: __('Deleting orphaned asset metadata...'), + steps: $flatOrphanedMetaFiles, + callback: function ($metaFile, $progress) { + /** @var AssetsContainer|null $container */ + $container = AssetContainer::find($metaFile['container']); + + if ($container) { + $container->disk()->delete($metaFile['path']); + } + + $progress->advance(); + } + ); + + $orphanedMetaFilesByContainer->each(function ($metaFiles, $containerHandle) { + if (! $container = AssetContainer::find($containerHandle)) { + return; + } + + $this->deleteEmptyMetaDirectories($container, $metaFiles); + }); + + $this->components->info(__('Deleted :count orphaned metadata :files.', [ + 'count' => $totalOrphanedMetaFiles, + 'files' => Str::plural('file', $totalOrphanedMetaFiles), + ])); + + return self::SUCCESS; + } + + private function getContainers(): Collection + { + if (! $container = $this->argument('container')) { + return AssetContainer::all(); + } + + if (! $container = AssetContainer::find($container)) { + throw new \InvalidArgumentException('Invalid container'); + } + + return collect([$container]); + } + + private function orphanedMetaFiles(AssetsContainer $container): Collection + { + $assetPaths = $container->files()->flip(); + + return $container->metaFiles() + ->filter(fn ($path) => Str::endsWith($path, '.yaml')) + ->filter(fn ($metaPath) => ! $assetPaths->has($this->metaPathToAssetPath($metaPath))) + ->values(); + } + + private function metaPathToAssetPath(string $metaPath): string + { + $pathWithoutYamlExtension = Str::endsWith($metaPath, '.yaml') + ? substr($metaPath, 0, -5) + : $metaPath; + $pathWithoutMetaDirectory = str_replace('/.meta/', '/', $pathWithoutYamlExtension); + + if (Str::startsWith($pathWithoutMetaDirectory, '.meta/')) { + $pathWithoutMetaDirectory = Str::replaceFirst('.meta/', '', $pathWithoutMetaDirectory); + } + + return ltrim($pathWithoutMetaDirectory, '/'); + } + + private function deleteEmptyMetaDirectories(AssetsContainer $container, Collection $metaFiles): void + { + $metaDirectories = $metaFiles + ->map(fn ($metaFile) => dirname($metaFile)) + ->unique() + ->sortByDesc(fn ($dir) => substr_count($dir, '/')); + + $metaDirectories->each(function ($metaDirectory) use ($container) { + $disk = $container->disk(); + + if (! $disk->exists($metaDirectory)) { + return; + } + + if ($disk->isEmpty($metaDirectory)) { + $disk->filesystem()->deleteDirectory($metaDirectory); + } + }); + } +} diff --git a/src/Providers/ConsoleServiceProvider.php b/src/Providers/ConsoleServiceProvider.php index 2720b1fca85..8d058c405ed 100644 --- a/src/Providers/ConsoleServiceProvider.php +++ b/src/Providers/ConsoleServiceProvider.php @@ -14,6 +14,7 @@ class ConsoleServiceProvider extends ServiceProvider Commands\AssetsCacheClear::class, Commands\AssetsGeneratePresets::class, Commands\AssetsMeta::class, + Commands\AssetsMetaClean::class, Commands\GlideClear::class, Commands\Install::class, Commands\InstallCollaboration::class, diff --git a/tests/Console/Commands/AssetsMetaCleanTest.php b/tests/Console/Commands/AssetsMetaCleanTest.php new file mode 100644 index 00000000000..8ca6152906c --- /dev/null +++ b/tests/Console/Commands/AssetsMetaCleanTest.php @@ -0,0 +1,141 @@ +containerWithDisk('test', 'test'); + $this->mockContainers($container); + + Storage::disk('test')->put('foo/.meta/bar.txt.yaml', 'size: 123'); + + $this->assertTrue(Storage::disk('test')->exists('foo/.meta/bar.txt.yaml')); + $this->assertTrue(Storage::disk('test')->exists('foo/.meta')); + + $this->artisan('statamic:assets:meta-clean test') + ->expectsOutputToContain('Deleted 1 orphaned metadata file.'); + + $this->assertFalse(Storage::disk('test')->exists('foo/.meta/bar.txt.yaml')); + $this->assertFalse(Storage::disk('test')->exists('foo/.meta')); + } + + #[Test] + public function it_preserves_meta_files_with_matching_assets() + { + $container = $this->containerWithDisk('test', 'test'); + $this->mockContainers($container); + + Storage::disk('test')->put('foo/bar.txt', 'bar'); + Storage::disk('test')->put('foo/.meta/bar.txt.yaml', 'size: 123'); + + $this->artisan('statamic:assets:meta-clean test') + ->expectsOutputToContain('No orphaned metadata files were found.'); + + $this->assertTrue(Storage::disk('test')->exists('foo/.meta/bar.txt.yaml')); + } + + #[Test] + public function dry_run_lists_orphaned_files_without_deleting_them() + { + $container = $this->containerWithDisk('test', 'test'); + $this->mockContainers($container); + + Storage::disk('test')->put('.meta/root.txt.yaml', 'size: 123'); + + $this->artisan('statamic:assets:meta-clean test --dry-run') + ->expectsOutputToContain('Found 1 orphaned metadata file.') + ->expectsOutputToContain('[test] .meta/root.txt.yaml'); + + $this->assertTrue(Storage::disk('test')->exists('.meta/root.txt.yaml')); + } + + #[Test] + public function it_only_cleans_the_requested_container() + { + $one = $this->containerWithDisk('one', 'test'); + $two = $this->containerWithDisk('two', 'test_two'); + $this->mockContainers($one, $two); + + Storage::disk('test')->put('foo/.meta/one.jpg.yaml', 'size: 1'); + Storage::disk('test_two')->put('foo/.meta/two.jpg.yaml', 'size: 2'); + + $this->artisan('statamic:assets:meta-clean one') + ->expectsOutputToContain('Deleted 1 orphaned metadata file.'); + + $this->assertFalse(Storage::disk('test')->exists('foo/.meta/one.jpg.yaml')); + $this->assertTrue(Storage::disk('test_two')->exists('foo/.meta/two.jpg.yaml')); + } + + #[Test] + public function it_cleans_all_containers_when_no_container_argument_is_provided() + { + $one = $this->containerWithDisk('one', 'test'); + $two = $this->containerWithDisk('two', 'test_two'); + $this->mockContainers($one, $two); + + Storage::disk('test')->put('foo/.meta/one.jpg.yaml', 'size: 1'); + Storage::disk('test_two')->put('foo/.meta/two.jpg.yaml', 'size: 2'); + + $this->artisan('statamic:assets:meta-clean') + ->expectsOutputToContain('Deleted 2 orphaned metadata files.'); + + $this->assertFalse(Storage::disk('test')->exists('foo/.meta/one.jpg.yaml')); + $this->assertFalse(Storage::disk('test_two')->exists('foo/.meta/two.jpg.yaml')); + } + + #[Test] + public function it_detects_orphaned_meta_files_in_root_and_nested_meta_directories() + { + $container = $this->containerWithDisk('test', 'test'); + $this->mockContainers($container); + + Storage::disk('test')->put('.meta/root.jpg.yaml', 'size: 1'); + Storage::disk('test')->put('foo/.meta/nested.jpg.yaml', 'size: 2'); + + $this->artisan('statamic:assets:meta-clean test') + ->expectsOutputToContain('Deleted 2 orphaned metadata files.'); + + $this->assertFalse(Storage::disk('test')->exists('.meta/root.jpg.yaml')); + $this->assertFalse(Storage::disk('test')->exists('foo/.meta/nested.jpg.yaml')); + } + + private function containerWithDisk(string $handle, string $disk): AssetContainer + { + return (new AssetContainer)->handle($handle)->disk($disk); + } + + private function mockContainers(AssetContainer ...$containers): void + { + $containersByHandle = collect($containers)->mapWithKeys(fn ($container) => [ + $container->handle() => $container, + ]); + + AssetContainerFacade::partialMock() + ->shouldReceive('all') + ->andReturn(collect($containers)); + + AssetContainerFacade::partialMock() + ->shouldReceive('findByHandle') + ->andReturnUsing(fn ($handle) => $containersByHandle->get($handle)); + } +} From 8bd6950fba3e973d518ff7e2f74368e64810c0fb Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 15:37:56 +0000 Subject: [PATCH 2/8] avoid mocking --- .../Console/Commands/AssetsMetaCleanTest.php | 45 ++++--------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/tests/Console/Commands/AssetsMetaCleanTest.php b/tests/Console/Commands/AssetsMetaCleanTest.php index 8ca6152906c..99476665e2a 100644 --- a/tests/Console/Commands/AssetsMetaCleanTest.php +++ b/tests/Console/Commands/AssetsMetaCleanTest.php @@ -4,8 +4,7 @@ use Illuminate\Support\Facades\Storage; use PHPUnit\Framework\Attributes\Test; -use Statamic\Assets\AssetContainer; -use Statamic\Facades\AssetContainer as AssetContainerFacade; +use Statamic\Facades\AssetContainer; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -24,8 +23,7 @@ public function setUp(): void #[Test] public function it_deletes_orphaned_meta_files_and_cleans_up_empty_meta_directories() { - $container = $this->containerWithDisk('test', 'test'); - $this->mockContainers($container); + AssetContainer::make('test')->disk('test')->save(); Storage::disk('test')->put('foo/.meta/bar.txt.yaml', 'size: 123'); @@ -42,8 +40,7 @@ public function it_deletes_orphaned_meta_files_and_cleans_up_empty_meta_director #[Test] public function it_preserves_meta_files_with_matching_assets() { - $container = $this->containerWithDisk('test', 'test'); - $this->mockContainers($container); + AssetContainer::make('test')->disk('test')->save(); Storage::disk('test')->put('foo/bar.txt', 'bar'); Storage::disk('test')->put('foo/.meta/bar.txt.yaml', 'size: 123'); @@ -57,8 +54,7 @@ public function it_preserves_meta_files_with_matching_assets() #[Test] public function dry_run_lists_orphaned_files_without_deleting_them() { - $container = $this->containerWithDisk('test', 'test'); - $this->mockContainers($container); + AssetContainer::make('test')->disk('test')->save(); Storage::disk('test')->put('.meta/root.txt.yaml', 'size: 123'); @@ -72,9 +68,8 @@ public function dry_run_lists_orphaned_files_without_deleting_them() #[Test] public function it_only_cleans_the_requested_container() { - $one = $this->containerWithDisk('one', 'test'); - $two = $this->containerWithDisk('two', 'test_two'); - $this->mockContainers($one, $two); + AssetContainer::make('one')->disk('test')->save(); + AssetContainer::make('two')->disk('test_two')->save(); Storage::disk('test')->put('foo/.meta/one.jpg.yaml', 'size: 1'); Storage::disk('test_two')->put('foo/.meta/two.jpg.yaml', 'size: 2'); @@ -89,9 +84,8 @@ public function it_only_cleans_the_requested_container() #[Test] public function it_cleans_all_containers_when_no_container_argument_is_provided() { - $one = $this->containerWithDisk('one', 'test'); - $two = $this->containerWithDisk('two', 'test_two'); - $this->mockContainers($one, $two); + AssetContainer::make('one')->disk('test')->save(); + AssetContainer::make('two')->disk('test_two')->save(); Storage::disk('test')->put('foo/.meta/one.jpg.yaml', 'size: 1'); Storage::disk('test_two')->put('foo/.meta/two.jpg.yaml', 'size: 2'); @@ -106,8 +100,7 @@ public function it_cleans_all_containers_when_no_container_argument_is_provided( #[Test] public function it_detects_orphaned_meta_files_in_root_and_nested_meta_directories() { - $container = $this->containerWithDisk('test', 'test'); - $this->mockContainers($container); + AssetContainer::make('test')->disk('test')->save(); Storage::disk('test')->put('.meta/root.jpg.yaml', 'size: 1'); Storage::disk('test')->put('foo/.meta/nested.jpg.yaml', 'size: 2'); @@ -118,24 +111,4 @@ public function it_detects_orphaned_meta_files_in_root_and_nested_meta_directori $this->assertFalse(Storage::disk('test')->exists('.meta/root.jpg.yaml')); $this->assertFalse(Storage::disk('test')->exists('foo/.meta/nested.jpg.yaml')); } - - private function containerWithDisk(string $handle, string $disk): AssetContainer - { - return (new AssetContainer)->handle($handle)->disk($disk); - } - - private function mockContainers(AssetContainer ...$containers): void - { - $containersByHandle = collect($containers)->mapWithKeys(fn ($container) => [ - $container->handle() => $container, - ]); - - AssetContainerFacade::partialMock() - ->shouldReceive('all') - ->andReturn(collect($containers)); - - AssetContainerFacade::partialMock() - ->shouldReceive('findByHandle') - ->andReturnUsing(fn ($handle) => $containersByHandle->get($handle)); - } } From bf7179a12d40506d815834cede2cea0c2cc34c28 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 15:40:17 +0000 Subject: [PATCH 3/8] reorder --- .../Console/Commands/AssetsMetaCleanTest.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/Console/Commands/AssetsMetaCleanTest.php b/tests/Console/Commands/AssetsMetaCleanTest.php index 99476665e2a..619ef077f58 100644 --- a/tests/Console/Commands/AssetsMetaCleanTest.php +++ b/tests/Console/Commands/AssetsMetaCleanTest.php @@ -20,6 +20,20 @@ public function setUp(): void Storage::fake('test_two'); } + #[Test] + public function dry_run_lists_orphaned_files_without_deleting_them() + { + AssetContainer::make('test')->disk('test')->save(); + + Storage::disk('test')->put('.meta/root.txt.yaml', 'size: 123'); + + $this->artisan('statamic:assets:meta-clean test --dry-run') + ->expectsOutputToContain('Found 1 orphaned metadata file.') + ->expectsOutputToContain('[test] .meta/root.txt.yaml'); + + $this->assertTrue(Storage::disk('test')->exists('.meta/root.txt.yaml')); + } + #[Test] public function it_deletes_orphaned_meta_files_and_cleans_up_empty_meta_directories() { @@ -51,20 +65,6 @@ public function it_preserves_meta_files_with_matching_assets() $this->assertTrue(Storage::disk('test')->exists('foo/.meta/bar.txt.yaml')); } - #[Test] - public function dry_run_lists_orphaned_files_without_deleting_them() - { - AssetContainer::make('test')->disk('test')->save(); - - Storage::disk('test')->put('.meta/root.txt.yaml', 'size: 123'); - - $this->artisan('statamic:assets:meta-clean test --dry-run') - ->expectsOutputToContain('Found 1 orphaned metadata file.') - ->expectsOutputToContain('[test] .meta/root.txt.yaml'); - - $this->assertTrue(Storage::disk('test')->exists('.meta/root.txt.yaml')); - } - #[Test] public function it_only_cleans_the_requested_container() { From dd7a3f38639116e366fe33fff057f83543d15c7f Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 15:40:35 +0000 Subject: [PATCH 4/8] be consistent. use the constant --- src/Console/Commands/AssetsMetaClean.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php index 748311cbe6c..128b9f2042b 100644 --- a/src/Console/Commands/AssetsMetaClean.php +++ b/src/Console/Commands/AssetsMetaClean.php @@ -51,7 +51,7 @@ public function handle(): int $this->line("[{$metaFile['container']}] {$metaFile['path']}"); }); - return 0; + return self::SUCCESS; } progress( From 73ddf16a2d6baf1a43255ff5fcde13074f6c8437 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 15:40:41 +0000 Subject: [PATCH 5/8] simplify --- src/Console/Commands/AssetsMetaClean.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php index 128b9f2042b..4337e919302 100644 --- a/src/Console/Commands/AssetsMetaClean.php +++ b/src/Console/Commands/AssetsMetaClean.php @@ -91,11 +91,7 @@ private function getContainers(): Collection return AssetContainer::all(); } - if (! $container = AssetContainer::find($container)) { - throw new \InvalidArgumentException('Invalid container'); - } - - return collect([$container]); + return collect([AssetContainer::findOrFail($container)]); } private function orphanedMetaFiles(AssetsContainer $container): Collection From a96d8cddca24bb9346eac72866fa50c768a86ce3 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 15:57:18 +0000 Subject: [PATCH 6/8] avoid duplicate queries. only lookup containers once --- src/Console/Commands/AssetsMetaClean.php | 40 +++++++++--------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php index 4337e919302..76956618b95 100644 --- a/src/Console/Commands/AssetsMetaClean.php +++ b/src/Console/Commands/AssetsMetaClean.php @@ -17,34 +17,34 @@ class AssetsMetaClean extends Command protected $signature = 'statamic:assets:meta-clean { container? : Handle of a container } - {--dry-run : List orphaned files without deleting}'; + { --dry-run : List orphaned files without deleting }'; protected $description = 'Clean orphaned asset metadata files'; public function handle(): int { - $orphanedMetaFilesByContainer = $this->getContainers() - ->mapWithKeys(fn ($container) => [$container->handle() => $this->orphanedMetaFiles($container)]); + $containers = $this->getContainers()->keyBy->handle(); - $totalOrphanedMetaFiles = $orphanedMetaFilesByContainer->sum->count(); + $orphanedMetaFilesByContainer = $containers->map(fn ($container) => $this->orphanedMetaFiles($container)); + $orphanedMetaFilesCount = $orphanedMetaFilesByContainer->sum->count(); - if ($totalOrphanedMetaFiles === 0) { + if ($orphanedMetaFilesCount === 0) { $this->components->info(__('No orphaned metadata files were found.')); return self::SUCCESS; } $flatOrphanedMetaFiles = $orphanedMetaFilesByContainer - ->flatMap(fn ($paths, $containerHandle) => $paths->map(fn ($path) => [ - 'container' => $containerHandle, + ->flatMap(fn ($paths, $container) => $paths->map(fn ($path) => [ + 'container' => $container, 'path' => $path, ])) ->values(); if ($this->option('dry-run')) { $this->components->warn(__('Found :count orphaned metadata :files.', [ - 'count' => $totalOrphanedMetaFiles, - 'files' => Str::plural('file', $totalOrphanedMetaFiles), + 'count' => $orphanedMetaFilesCount, + 'files' => Str::plural('file', $orphanedMetaFilesCount), ])); $flatOrphanedMetaFiles->each(function ($metaFile) { @@ -57,29 +57,19 @@ public function handle(): int progress( label: __('Deleting orphaned asset metadata...'), steps: $flatOrphanedMetaFiles, - callback: function ($metaFile, $progress) { - /** @var AssetsContainer|null $container */ - $container = AssetContainer::find($metaFile['container']); - - if ($container) { - $container->disk()->delete($metaFile['path']); - } - + callback: function ($metaFile, $progress) use ($containers) { + $containers->get($metaFile['container'])->disk()->delete($metaFile['path']); $progress->advance(); } ); - $orphanedMetaFilesByContainer->each(function ($metaFiles, $containerHandle) { - if (! $container = AssetContainer::find($containerHandle)) { - return; - } - - $this->deleteEmptyMetaDirectories($container, $metaFiles); + $orphanedMetaFilesByContainer->each(function ($metaFiles, $containerHandle) use ($containers) { + $this->deleteEmptyMetaDirectories($containers->get($containerHandle), $metaFiles); }); $this->components->info(__('Deleted :count orphaned metadata :files.', [ - 'count' => $totalOrphanedMetaFiles, - 'files' => Str::plural('file', $totalOrphanedMetaFiles), + 'count' => $orphanedMetaFilesCount, + 'files' => Str::plural('file', $orphanedMetaFilesCount), ])); return self::SUCCESS; From ff694876e66dc30576e3a85490d09e1d79ef22fd Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 16:04:34 +0000 Subject: [PATCH 7/8] types --- src/Console/Commands/AssetsMetaClean.php | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php index 76956618b95..600dc3ac6e0 100644 --- a/src/Console/Commands/AssetsMetaClean.php +++ b/src/Console/Commands/AssetsMetaClean.php @@ -25,7 +25,7 @@ public function handle(): int { $containers = $this->getContainers()->keyBy->handle(); - $orphanedMetaFilesByContainer = $containers->map(fn ($container) => $this->orphanedMetaFiles($container)); + $orphanedMetaFilesByContainer = $containers->map(fn ($container) => $this->getOrphanedMetaFiles($container)); $orphanedMetaFilesCount = $orphanedMetaFilesByContainer->sum->count(); if ($orphanedMetaFilesCount === 0) { @@ -35,7 +35,7 @@ public function handle(): int } $flatOrphanedMetaFiles = $orphanedMetaFilesByContainer - ->flatMap(fn ($paths, $container) => $paths->map(fn ($path) => [ + ->flatMap(fn (Collection $paths, string $container) => $paths->map(fn ($path) => [ 'container' => $container, 'path' => $path, ])) @@ -47,7 +47,7 @@ public function handle(): int 'files' => Str::plural('file', $orphanedMetaFilesCount), ])); - $flatOrphanedMetaFiles->each(function ($metaFile) { + $flatOrphanedMetaFiles->each(function (array $metaFile) { $this->line("[{$metaFile['container']}] {$metaFile['path']}"); }); @@ -57,14 +57,14 @@ public function handle(): int progress( label: __('Deleting orphaned asset metadata...'), steps: $flatOrphanedMetaFiles, - callback: function ($metaFile, $progress) use ($containers) { + callback: function (array $metaFile, $progress) use ($containers) { $containers->get($metaFile['container'])->disk()->delete($metaFile['path']); $progress->advance(); } ); - $orphanedMetaFilesByContainer->each(function ($metaFiles, $containerHandle) use ($containers) { - $this->deleteEmptyMetaDirectories($containers->get($containerHandle), $metaFiles); + $orphanedMetaFilesByContainer->each(function (Collection $metaFiles, string $container) use ($containers) { + $this->deleteEmptyMetaDirectories($containers->get($container), $metaFiles); }); $this->components->info(__('Deleted :count orphaned metadata :files.', [ @@ -84,13 +84,13 @@ private function getContainers(): Collection return collect([AssetContainer::findOrFail($container)]); } - private function orphanedMetaFiles(AssetsContainer $container): Collection + private function getOrphanedMetaFiles(AssetsContainer $container): Collection { $assetPaths = $container->files()->flip(); return $container->metaFiles() - ->filter(fn ($path) => Str::endsWith($path, '.yaml')) - ->filter(fn ($metaPath) => ! $assetPaths->has($this->metaPathToAssetPath($metaPath))) + ->filter(fn (string $path) => Str::endsWith($path, '.yaml')) + ->reject(fn (string $path) => $assetPaths->has($this->metaPathToAssetPath($path))) ->values(); } @@ -99,6 +99,7 @@ private function metaPathToAssetPath(string $metaPath): string $pathWithoutYamlExtension = Str::endsWith($metaPath, '.yaml') ? substr($metaPath, 0, -5) : $metaPath; + $pathWithoutMetaDirectory = str_replace('/.meta/', '/', $pathWithoutYamlExtension); if (Str::startsWith($pathWithoutMetaDirectory, '.meta/')) { @@ -111,11 +112,11 @@ private function metaPathToAssetPath(string $metaPath): string private function deleteEmptyMetaDirectories(AssetsContainer $container, Collection $metaFiles): void { $metaDirectories = $metaFiles - ->map(fn ($metaFile) => dirname($metaFile)) + ->map(fn (string $metaFile) => dirname($metaFile)) ->unique() - ->sortByDesc(fn ($dir) => substr_count($dir, '/')); + ->sortByDesc(fn (string $directory) => substr_count($directory, '/')); - $metaDirectories->each(function ($metaDirectory) use ($container) { + $metaDirectories->each(function (string $metaDirectory) use ($container) { $disk = $container->disk(); if (! $disk->exists($metaDirectory)) { From 5a19d9ff5e3dfb37e3737f4d68a58e382228d7ae Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 27 Feb 2026 16:06:55 +0000 Subject: [PATCH 8/8] don't need to translate strings in commands --- src/Console/Commands/AssetsMetaClean.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Console/Commands/AssetsMetaClean.php b/src/Console/Commands/AssetsMetaClean.php index 600dc3ac6e0..734d31caf66 100644 --- a/src/Console/Commands/AssetsMetaClean.php +++ b/src/Console/Commands/AssetsMetaClean.php @@ -29,7 +29,7 @@ public function handle(): int $orphanedMetaFilesCount = $orphanedMetaFilesByContainer->sum->count(); if ($orphanedMetaFilesCount === 0) { - $this->components->info(__('No orphaned metadata files were found.')); + $this->components->info('No orphaned metadata files were found.'); return self::SUCCESS; } @@ -42,10 +42,7 @@ public function handle(): int ->values(); if ($this->option('dry-run')) { - $this->components->warn(__('Found :count orphaned metadata :files.', [ - 'count' => $orphanedMetaFilesCount, - 'files' => Str::plural('file', $orphanedMetaFilesCount), - ])); + $this->components->warn("Found {$orphanedMetaFilesCount} orphaned metadata ".Str::plural('file', $orphanedMetaFilesCount)); $flatOrphanedMetaFiles->each(function (array $metaFile) { $this->line("[{$metaFile['container']}] {$metaFile['path']}"); @@ -55,7 +52,7 @@ public function handle(): int } progress( - label: __('Deleting orphaned asset metadata...'), + label: 'Deleting orphaned asset metadata...', steps: $flatOrphanedMetaFiles, callback: function (array $metaFile, $progress) use ($containers) { $containers->get($metaFile['container'])->disk()->delete($metaFile['path']); @@ -67,10 +64,7 @@ public function handle(): int $this->deleteEmptyMetaDirectories($containers->get($container), $metaFiles); }); - $this->components->info(__('Deleted :count orphaned metadata :files.', [ - 'count' => $orphanedMetaFilesCount, - 'files' => Str::plural('file', $orphanedMetaFilesCount), - ])); + $this->components->warn("Deleted {$orphanedMetaFilesCount} orphaned metadata ".Str::plural('file', $orphanedMetaFilesCount)); return self::SUCCESS; }