From e2a9f30ed6d501ada0239d0b0865207745ad65bb Mon Sep 17 00:00:00 2001 From: Laurens Kuiper Date: Thu, 12 Mar 2026 11:28:05 +0100 Subject: [PATCH 1/2] feat(assets): add unique filename support to Asset::move() Add $unique parameter to move() for parity with rename(). When true, ensureUniqueFilename() generates a collision-free name in the target folder. Simplify rename() to delegate uniqueness logic to move(). --- src/Assets/Asset.php | 7 ++--- tests/Assets/AssetTest.php | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index bc9ca65a8a..dfa9b460da 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -744,9 +744,7 @@ public function containerHandle() */ public function rename($filename, $unique = false) { - $filename = $unique ? $this->ensureUniqueFilename($this->folder(), $filename) : $filename; - - return $this->move($this->folder(), $filename); + return $this->move($this->folder(), $filename, $unique); } /** @@ -756,9 +754,10 @@ public function rename($filename, $unique = false) * @param string|null $filename The new filename, if renaming. * @return $this */ - public function move($folder, $filename = null) + public function move($folder, $filename = null, $unique = false) { $filename = Uploader::getSafeFilename($filename ?: $this->filename()); + $filename = $unique ? $this->ensureUniqueFilename($folder, $filename) : $filename; $oldPath = $this->path(); $oldMetaPath = $this->metaPath(); $newPath = Str::removeLeft(Path::tidy($folder.'/'.$filename.'.'.pathinfo($oldPath, PATHINFO_EXTENSION)), '/'); diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index 00046ae0f5..9ed3a9d180 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -1260,6 +1260,70 @@ public function it_doesnt_lowercase_moved_files_when_configured() ], $container->assets('/', true)->map->path()->all()); } + #[Test] + public function it_can_be_moved_to_another_folder_with_a_unique_filename_when_conflict_exists() + { + Storage::fake('local'); + $disk = Storage::disk('local'); + $disk->put('old/asset.txt', 'The asset contents'); + $disk->put('new/asset.txt', 'Existing asset'); + $disk->put('new/asset-1.txt', 'Another existing asset'); + $container = Facades\AssetContainer::make('test')->disk('local'); + Facades\AssetContainer::shouldReceive('save')->with($container); + Facades\AssetContainer::shouldReceive('findByHandle')->with('test')->andReturn($container); + $asset = $container->makeAsset('old/asset.txt')->data(['foo' => 'bar']); + $asset->save(); + + $return = $asset->move('new', null, true); + + $this->assertEquals($asset, $return); + $disk->assertMissing('old/asset.txt'); + $disk->assertExists('new/asset-2.txt'); + $this->assertEquals('new/asset-2.txt', $asset->path()); + } + + #[Test] + public function it_can_be_moved_to_another_folder_with_unique_flag_without_renaming_when_no_conflict() + { + Storage::fake('local'); + $disk = Storage::disk('local'); + $disk->put('old/asset.txt', 'The asset contents'); + $container = Facades\AssetContainer::make('test')->disk('local'); + Facades\AssetContainer::shouldReceive('save')->with($container); + Facades\AssetContainer::shouldReceive('findByHandle')->with('test')->andReturn($container); + $asset = $container->makeAsset('old/asset.txt')->data(['foo' => 'bar']); + $asset->save(); + + $return = $asset->move('new', null, true); + + $this->assertEquals($asset, $return); + $disk->assertMissing('old/asset.txt'); + $disk->assertExists('new/asset.txt'); + $this->assertEquals('new/asset.txt', $asset->path()); + } + + #[Test] + public function it_does_not_ensure_unique_filename_when_moving_by_default() + { + Storage::fake('local'); + $disk = Storage::disk('local'); + $disk->put('old/asset.txt', 'The asset contents'); + $disk->put('new/asset.txt', 'Existing asset'); + $container = Facades\AssetContainer::make('test')->disk('local'); + Facades\AssetContainer::shouldReceive('save')->with($container); + Facades\AssetContainer::shouldReceive('findByHandle')->with('test')->andReturn($container); + $asset = $container->makeAsset('old/asset.txt')->data(['foo' => 'bar']); + $asset->save(); + + $return = $asset->move('new'); + + $this->assertEquals($asset, $return); + $disk->assertMissing('old/asset.txt'); + // Without unique flag, it overwrites the existing file + $disk->assertExists('new/asset.txt'); + $this->assertEquals('new/asset.txt', $asset->path()); + } + #[Test] public function it_renames() { From 3d7c602b35757df77c1c5b3158f3d8b9915d7627 Mon Sep 17 00:00:00 2001 From: Laurens Kuiper Date: Fri, 13 Mar 2026 15:47:17 +0100 Subject: [PATCH 2/2] refactor(assets): use moveUnique() method instead of $unique parameter on move() Replace the breaking $unique parameter on move() with a dedicated moveUnique() method. Update rename() to delegate to moveUnique() when unique filenames are needed. --- src/Assets/Asset.php | 24 +++++++++++++++++++++--- tests/Assets/AssetTest.php | 8 ++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index dfa9b460da..bab46f0a82 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -744,7 +744,11 @@ public function containerHandle() */ public function rename($filename, $unique = false) { - return $this->move($this->folder(), $filename, $unique); + if ($unique) { + return $this->moveUnique($this->folder(), $filename); + } + + return $this->move($this->folder(), $filename); } /** @@ -754,10 +758,9 @@ public function rename($filename, $unique = false) * @param string|null $filename The new filename, if renaming. * @return $this */ - public function move($folder, $filename = null, $unique = false) + public function move($folder, $filename = null) { $filename = Uploader::getSafeFilename($filename ?: $this->filename()); - $filename = $unique ? $this->ensureUniqueFilename($folder, $filename) : $filename; $oldPath = $this->path(); $oldMetaPath = $this->metaPath(); $newPath = Str::removeLeft(Path::tidy($folder.'/'.$filename.'.'.pathinfo($oldPath, PATHINFO_EXTENSION)), '/'); @@ -776,6 +779,21 @@ public function move($folder, $filename = null, $unique = false) return $this; } + /** + * Move the asset to a different location with a unique filename. + * + * @param string $folder The folder relative to the container. + * @param string|null $filename The new filename, if renaming. + * @return $this + */ + public function moveUnique($folder, $filename = null) + { + $filename = Uploader::getSafeFilename($filename ?: $this->filename()); + $filename = $this->ensureUniqueFilename($folder, $filename); + + return $this->move($folder, $filename); + } + public function moveQuietly($folder, $filename = null) { $this->withEvents = false; diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index 9ed3a9d180..07504afa1a 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -1261,7 +1261,7 @@ public function it_doesnt_lowercase_moved_files_when_configured() } #[Test] - public function it_can_be_moved_to_another_folder_with_a_unique_filename_when_conflict_exists() + public function it_can_be_moved_uniquely_to_another_folder_when_conflict_exists() { Storage::fake('local'); $disk = Storage::disk('local'); @@ -1274,7 +1274,7 @@ public function it_can_be_moved_to_another_folder_with_a_unique_filename_when_co $asset = $container->makeAsset('old/asset.txt')->data(['foo' => 'bar']); $asset->save(); - $return = $asset->move('new', null, true); + $return = $asset->moveUnique('new'); $this->assertEquals($asset, $return); $disk->assertMissing('old/asset.txt'); @@ -1283,7 +1283,7 @@ public function it_can_be_moved_to_another_folder_with_a_unique_filename_when_co } #[Test] - public function it_can_be_moved_to_another_folder_with_unique_flag_without_renaming_when_no_conflict() + public function it_can_be_moved_uniquely_to_another_folder_without_renaming_when_no_conflict() { Storage::fake('local'); $disk = Storage::disk('local'); @@ -1294,7 +1294,7 @@ public function it_can_be_moved_to_another_folder_with_unique_flag_without_renam $asset = $container->makeAsset('old/asset.txt')->data(['foo' => 'bar']); $asset->save(); - $return = $asset->move('new', null, true); + $return = $asset->moveUnique('new'); $this->assertEquals($asset, $return); $disk->assertMissing('old/asset.txt');