Skip to content

Commit a639108

Browse files
authored
Feature/new custom delete fix #3190 (#3252)
1 parent ae3107b commit a639108

File tree

10 files changed

+363
-19
lines changed

10 files changed

+363
-19
lines changed

config/media-library.php

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@
7373
*/
7474
'path_generator' => Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator::class,
7575

76+
/*
77+
* The class that contains the strategy for determining how to remove files.
78+
*/
79+
'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover::class,
80+
7681
/*
7782
* Here you can specify which path generator should be used for the given class.
7883
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: Using a custom file removal strategy
3+
weight: 5
4+
---
5+
6+
By default, files will be stored inside a directory that uses the `id` of its `Media`-object as a name. Given this default file and folder structure, the `DefaultFileRemover` option simply gets the root folder name and deletes it.
7+
8+
In cases where you need to use a custom directory structure, the `DefaultFileRemover` can cause problems. For example, if you have a directory structure like this:
9+
10+
11+
```
12+
media
13+
---- 2023/09
14+
------ file.jpg
15+
------ second.jpg
16+
------ conversions
17+
--------- file-small.jpg
18+
--------- file-medium.jpg
19+
--------- file-big.jpg
20+
--------- second-small.jpg
21+
--------- second-medium.jpg
22+
--------- second-big.jpg
23+
...
24+
```
25+
26+
Using the `DefaultFileRemover` will delete the entire `2023` directory, which is not what you want. So we would use a custom file remover to delete only the files that are no longer needed.
27+
28+
29+
### Extending file remover functionality
30+
31+
32+
Let's take a look at the interface:
33+
34+
```php
35+
<?php
36+
37+
namespace Spatie\MediaLibrary\Support\FileRemover;
38+
39+
use Illuminate\Contracts\Filesystem\Factory;
40+
use Spatie\MediaLibrary\MediaCollections\Filesystem;
41+
use Spatie\MediaLibrary\MediaCollections\Models\Media;
42+
43+
interface FileRemover
44+
{
45+
public function __construct(Filesystem $mediaFileSystem, Factory $filesystem);
46+
47+
/*
48+
* Remove all files relating to the media model.
49+
*/
50+
public function removeAllFiles(Media $media): void;
51+
52+
/*
53+
* Remove responsive files relating to the media model.
54+
*/
55+
public function removeResponsiveImages(Media $media, string $conversionName): void;
56+
57+
/*
58+
* Remove a file relating to the media model.
59+
*/
60+
public function removeFile(string $path, string $disk): void;
61+
62+
}
63+
64+
```
65+
You may use create your own custom file remover by implementing the `FileRemover` interface.
66+
67+
### Here to help
68+
69+
There is also now a second option available within media library for file remover functionality. Based on the above directory structure, we can use `FileBaseFileRemover`.
70+
71+
```php
72+
// config/media-library.php
73+
74+
/*
75+
* The class that contains the strategy for determining how to remove files.
76+
*/
77+
'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\FileBaseFileRemover::class,
78+
```
79+
80+
This strategy works by locating the exact path of the image and conversions, and explicitly removing those files only, instead of purging a base directory.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Spatie\MediaLibrary\MediaCollections\Exceptions;
4+
5+
use Exception;
6+
use Spatie\MediaLibrary\Support\FileRemover\FileRemover;
7+
8+
class InvalidFileRemover extends Exception
9+
{
10+
public static function doesntExist(string $class): self
11+
{
12+
return new static("File remover class `{$class}` doesn't exist");
13+
}
14+
15+
public static function doesNotImplementPathGenerator(string $class): self
16+
{
17+
$fileRemoverClass = FileRemover::class;
18+
19+
return new static("File remover class `{$class}` must implement `$fileRemoverClass}`");
20+
}
21+
}

src/MediaCollections/Filesystem.php

+6-19
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Support\Str;
99
use Spatie\MediaLibrary\Conversions\ConversionCollection;
1010
use Spatie\MediaLibrary\Conversions\FileManipulator;
11+
use Spatie\MediaLibrary\Support\FileRemover\FileRemoverFactory;
1112
use Spatie\MediaLibrary\MediaCollections\Events\MediaHasBeenAdded;
1213
use Spatie\MediaLibrary\MediaCollections\Exceptions\DiskCannotBeAccessed;
1314
use Spatie\MediaLibrary\MediaCollections\Models\Media;
@@ -215,30 +216,16 @@ public function copyFromMediaLibrary(Media $media, string $targetFile): string
215216

216217
public function removeAllFiles(Media $media): void
217218
{
218-
$mediaDirectory = $this->getMediaDirectory($media);
219-
220-
if ($media->disk !== $media->conversions_disk) {
221-
$this->filesystem->disk($media->disk)->deleteDirectory($mediaDirectory);
222-
}
219+
$fileRemover = FileRemoverFactory::create($media);
223220

224-
$conversionsDirectory = $this->getMediaDirectory($media, 'conversions');
225-
226-
$responsiveImagesDirectory = $this->getMediaDirectory($media, 'responsiveImages');
227-
228-
collect([$mediaDirectory, $conversionsDirectory, $responsiveImagesDirectory])
229-
->unique()
230-
->each(function (string $directory) use ($media) {
231-
try {
232-
$this->filesystem->disk($media->conversions_disk)->deleteDirectory($directory);
233-
} catch (Exception $exception) {
234-
report($exception);
235-
}
236-
});
221+
$fileRemover->removeAllFiles($media);
237222
}
238223

239224
public function removeFile(Media $media, string $path): void
240225
{
241-
$this->filesystem->disk($media->disk)->delete($path);
226+
$fileRemover = FileRemoverFactory::create($media);
227+
228+
$fileRemover->removeFile($path, $media->disk);
242229
}
243230

244231
public function removeResponsiveImages(Media $media, string $conversionName = 'media_library_original'): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Spatie\MediaLibrary\Support\FileRemover;
4+
5+
use Exception;
6+
use Illuminate\Support\Str;
7+
use Illuminate\Contracts\Filesystem\Factory;
8+
use Spatie\MediaLibrary\MediaCollections\Filesystem;
9+
use Spatie\MediaLibrary\MediaCollections\Models\Media;
10+
11+
class DefaultFileRemover implements FileRemover
12+
{
13+
public function __construct(protected Filesystem $mediaFileSystem, protected Factory $filesystem)
14+
{}
15+
16+
public function removeAllFiles(Media $media): void
17+
{
18+
$mediaDirectory = $this->mediaFileSystem->getMediaDirectory($media);
19+
20+
if ($media->disk !== $media->conversions_disk) {
21+
$this->filesystem->disk($media->disk)->deleteDirectory($mediaDirectory);
22+
}
23+
24+
$conversionsDirectory = $this->mediaFileSystem->getMediaDirectory($media, 'conversions');
25+
26+
$responsiveImagesDirectory = $this->mediaFileSystem->getMediaDirectory($media, 'responsiveImages');
27+
28+
collect([$mediaDirectory, $conversionsDirectory, $responsiveImagesDirectory])
29+
->unique()
30+
->each(function (string $directory) use ($media) {
31+
try {
32+
$this->filesystem->disk($media->conversions_disk)->deleteDirectory($directory);
33+
} catch (Exception $exception) {
34+
report($exception);
35+
}
36+
});
37+
}
38+
39+
public function removeResponsiveImages(Media $media, string $conversionName): void
40+
{
41+
$responsiveImagesDirectory = $this->mediaFileSystem->getResponsiveImagesDirectory($media);
42+
43+
$allFilePaths = $this->filesystem->disk($media->disk)->allFiles($responsiveImagesDirectory);
44+
45+
$responsiveImagePaths = array_filter(
46+
$allFilePaths,
47+
fn (string $path) => Str::contains($path, $conversionName)
48+
);
49+
50+
$this->filesystem->disk($media->disk)->delete($responsiveImagePaths);
51+
}
52+
53+
public function removeFile(string $path, string $disk): void
54+
{
55+
$this->filesystem->disk($disk)->delete($path);
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Spatie\MediaLibrary\Support\FileRemover;
4+
5+
use Illuminate\Contracts\Filesystem\Factory;
6+
use Spatie\MediaLibrary\MediaCollections\Filesystem;
7+
use Spatie\MediaLibrary\MediaCollections\Models\Media;
8+
use Spatie\MediaLibrary\Support\FileRemover\FileRemover;
9+
use Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover;
10+
11+
class FileBaseFileRemover extends DefaultFileRemover implements FileRemover
12+
{
13+
public function __construct(protected Filesystem $mediaFileSystem, protected Factory $filesystem)
14+
{}
15+
16+
public function removeAllFiles(Media $media): void
17+
{
18+
$this->removeFile($this->mediaFileSystem->getMediaDirectory($media). $media->file_name, $media->disk);
19+
20+
$this->removeConvertedImages($media);
21+
}
22+
23+
public function removeConvertedImages(Media $media): void
24+
{
25+
collect($media->getMediaConversionNames())->each(function ($conversionName) use ($media) {
26+
$this->removeFile(
27+
path: $media->getPathRelativeToRoot($conversionName),
28+
disk: $media->conversions_disk
29+
);
30+
31+
$this->mediaFileSystem->removeResponsiveImages($media, $conversionName);
32+
});
33+
}
34+
35+
public function removeFile(string $path, string $disk): void
36+
{
37+
$this->filesystem->disk($disk)->delete($path);
38+
}
39+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Spatie\MediaLibrary\Support\FileRemover;
4+
5+
use Illuminate\Contracts\Filesystem\Factory;
6+
use Spatie\MediaLibrary\MediaCollections\Filesystem;
7+
use Spatie\MediaLibrary\MediaCollections\Models\Media;
8+
9+
interface FileRemover
10+
{
11+
public function __construct(Filesystem $mediaFileSystem, Factory $filesystem);
12+
13+
/*
14+
* Remove all files relating to the media model.
15+
*/
16+
public function removeAllFiles(Media $media): void;
17+
18+
/*
19+
* Remove responsive files relating to the media model.
20+
*/
21+
public function removeResponsiveImages(Media $media, string $conversionName): void;
22+
23+
/*
24+
* Remove a file relating to the media model.
25+
*/
26+
public function removeFile(string $path, string $disk): void;
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Spatie\MediaLibrary\Support\FileRemover;
4+
5+
use Spatie\MediaLibrary\MediaCollections\Models\Media;
6+
use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidFileRemover;
7+
8+
class FileRemoverFactory
9+
{
10+
public static function create(Media $media): FileRemover
11+
{
12+
$fileRemoverClass = config('media-library.file_remover_class');
13+
14+
static::guardAgainstInvalidFileRemover($fileRemoverClass);
15+
16+
return app($fileRemoverClass);
17+
}
18+
19+
protected static function guardAgainstInvalidFileRemover(string $fileRemoverClass): void
20+
{
21+
if (!class_exists($fileRemoverClass)) {
22+
throw InvalidFileRemover::doesntExist($fileRemoverClass);
23+
}
24+
25+
if (!is_subclass_of($fileRemoverClass, FileRemover::class)) {
26+
throw InvalidFileRemover::doesNotImplementFileRemover($fileRemoverClass);
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)