diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 039b43007..ba4f3cb4f 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -284,9 +284,20 @@
+
+
+
+
+
+
+ filesCollection->updateMany(
+ ['filename' => $filename],
+ ['$set' => ['filename' => $newFilename]],
+ )->getMatchedCount()]]>
+
diff --git a/src/GridFS/Bucket.php b/src/GridFS/Bucket.php
index 56a4e26e2..85046906c 100644
--- a/src/GridFS/Bucket.php
+++ b/src/GridFS/Bucket.php
@@ -242,6 +242,23 @@ public function delete(mixed $id)
}
}
+ /**
+ * Delete all the revisions of a file name from the GridFS bucket.
+ *
+ * @param string $filename Filename
+ *
+ * @throws FileNotFoundException if no file could be selected
+ * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
+ */
+ public function deleteByName(string $filename): void
+ {
+ $count = $this->collectionWrapper->deleteFileAndChunksByFilename($filename);
+
+ if ($count === 0) {
+ throw FileNotFoundException::byFilename($filename);
+ }
+ }
+
/**
* Writes the contents of a GridFS file to a writable stream.
*
@@ -648,6 +665,24 @@ public function rename(mixed $id, string $newFilename)
}
}
+ /**
+ * Renames all the revisions of a file name in the GridFS bucket.
+ *
+ * @param string $filename Filename
+ * @param string $newFilename New filename
+ *
+ * @throws FileNotFoundException if no file could be selected
+ * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
+ */
+ public function renameByName(string $filename, string $newFilename): void
+ {
+ $count = $this->collectionWrapper->updateFilenameForFilename($filename, $newFilename);
+
+ if ($count === 0) {
+ throw FileNotFoundException::byFilename($filename);
+ }
+ }
+
/**
* Writes the contents of a readable stream to a GridFS file.
*
diff --git a/src/GridFS/CollectionWrapper.php b/src/GridFS/CollectionWrapper.php
index 0c6b9b96c..09b06ba23 100644
--- a/src/GridFS/CollectionWrapper.php
+++ b/src/GridFS/CollectionWrapper.php
@@ -74,7 +74,7 @@ public function deleteChunksByFilesId(mixed $id): void
/**
* Delete all GridFS files and chunks for a given filename.
*/
- public function deleteFileAndChunksByFilename(string $filename): ?int
+ public function deleteFileAndChunksByFilename(string $filename): int
{
/** @var iterable $files */
$files = $this->findFiles(['filename' => $filename], [
@@ -150,9 +150,6 @@ public function findChunksByFileId(mixed $id, int $fromChunk = 0)
*/
public function findFileByFilenameAndRevision(string $filename, int $revision): ?object
{
- $filename = $filename;
- $revision = $revision;
-
if ($revision < 0) {
$skip = abs($revision) - 1;
$sortOrder = -1;
@@ -266,7 +263,7 @@ public function insertFile(array|object $file): void
/**
* Updates the filename field in the file document for all the files with a given filename.
*/
- public function updateFilenameForFilename(string $filename, string $newFilename): ?int
+ public function updateFilenameForFilename(string $filename, string $newFilename): int
{
return $this->filesCollection->updateMany(
['filename' => $filename],
diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php
index 0b85b8d9a..8faef8e38 100644
--- a/tests/GridFS/BucketFunctionalTest.php
+++ b/tests/GridFS/BucketFunctionalTest.php
@@ -160,6 +160,34 @@ public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expected
$this->assertCollectionCount($this->chunksCollection, 0);
}
+ public function testDeleteByName(): void
+ {
+ $this->bucket->uploadFromStream('filename', self::createStream('foobar1'));
+ $this->bucket->uploadFromStream('filename', self::createStream('foobar2'));
+ $this->bucket->uploadFromStream('filename', self::createStream('foobar3'));
+
+ $this->bucket->uploadFromStream('other', self::createStream('foobar'));
+
+ $this->assertCollectionCount($this->filesCollection, 4);
+ $this->assertCollectionCount($this->chunksCollection, 4);
+
+ $this->bucket->deleteByName('filename');
+
+ $this->assertCollectionCount($this->filesCollection, 1);
+ $this->assertCollectionCount($this->chunksCollection, 1);
+
+ $this->bucket->deleteByName('other');
+
+ $this->assertCollectionCount($this->filesCollection, 0);
+ $this->assertCollectionCount($this->chunksCollection, 0);
+ }
+
+ public function testDeleteByNameShouldRequireFileToExist(): void
+ {
+ $this->expectException(FileNotFoundException::class);
+ $this->bucket->deleteByName('nonexistent-name');
+ }
+
public function testDownloadingFileWithMissingChunk(): void
{
$id = $this->bucket->uploadFromStream('filename', self::createStream('foobar'));
@@ -723,6 +751,24 @@ public function testRenameShouldRequireFileToExist(): void
$this->bucket->rename('nonexistent-id', 'b');
}
+ public function testRenameByName(): void
+ {
+ $this->bucket->uploadFromStream('filename', self::createStream('foo'));
+ $this->bucket->uploadFromStream('filename', self::createStream('foo'));
+ $this->bucket->uploadFromStream('filename', self::createStream('foo'));
+
+ $this->bucket->renameByName('filename', 'newname');
+
+ $this->assertNull($this->bucket->findOne(['filename' => 'filename']), 'No file has the old name');
+ $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('newname'));
+ }
+
+ public function testRenameByNameShouldRequireFileToExist(): void
+ {
+ $this->expectException(FileNotFoundException::class);
+ $this->bucket->renameByName('nonexistent-name', 'b');
+ }
+
public function testUploadFromStream(): void
{
$options = [
diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php
index 390e278be..d2550f498 100644
--- a/tests/UnifiedSpecTests/Operation.php
+++ b/tests/UnifiedSpecTests/Operation.php
@@ -789,6 +789,12 @@ private function executeForBucket(Bucket $bucket)
return $bucket->delete($args['id']);
+ case 'deleteByName':
+ assertArrayHasKey('filename', $args);
+ assertIsString($args['filename']);
+
+ return $bucket->deleteByName($args['filename']);
+
case 'downloadByName':
assertArrayHasKey('filename', $args);
assertIsString($args['filename']);
@@ -812,6 +818,14 @@ private function executeForBucket(Bucket $bucket)
return null;
+ case 'renameByName':
+ assertArrayHasKey('filename', $args);
+ assertArrayHasKey('newFilename', $args);
+ assertIsString($args['filename']);
+ assertIsString($args['newFilename']);
+
+ return $bucket->renameByName($args['filename'], $args['newFilename']);
+
case 'uploadWithId':
assertArrayHasKey('id', $args);
$args['_id'] = $args['id'];
diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php
index e09e3f942..b0d575d39 100644
--- a/tests/UnifiedSpecTests/Util.php
+++ b/tests/UnifiedSpecTests/Util.php
@@ -132,9 +132,11 @@ final class Util
],
Bucket::class => [
'delete' => ['id'],
+ 'deleteByName' => ['filename'],
'downloadByName' => ['filename', 'revision'],
'download' => ['id'],
'rename' => ['id', 'newFilename'],
+ 'renameByName' => ['filename', 'newFilename'],
'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'],
'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'],
],