Completed
Push — master ( c2f76d...03586b )
by Freek
11:35
created

CleanCommand::deleteOrphanedDirectories()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
cc 5
eloc 14
nc 4
nop 0
1
<?php
2
3
namespace Spatie\MediaLibrary\Commands;
4
5
use Spatie\MediaLibrary\Models\Media;
6
use Illuminate\Console\Command;
7
use Illuminate\Console\ConfirmableTrait;
8
use Spatie\MediaLibrary\FileManipulator;
9
use Spatie\MediaLibrary\MediaRepository;
10
use Illuminate\Contracts\Filesystem\Factory;
11
use Illuminate\Database\Eloquent\Collection;
12
use Spatie\MediaLibrary\Conversion\Conversion;
13
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded;
14
use Spatie\MediaLibrary\Conversion\ConversionCollection;
15
use Spatie\MediaLibrary\PathGenerator\BasePathGenerator;
16
use Spatie\MediaLibrary\ResponsiveImages\ResponsiveImage;
17
use Spatie\MediaLibrary\ResponsiveImages\RegisteredResponsiveImages;
18
19
class CleanCommand extends Command
20
{
21
    use ConfirmableTrait;
22
23
    protected $signature = 'medialibrary:clean {modelType?} {collectionName?} {disk?}
24
    {--dry-run : List files that will be removed without removing them},
25
    {--force : Force the operation to run when in production}';
26
27
    protected $description = 'Clean deprecated conversions and files without related model.';
28
29
    /** @var \Spatie\MediaLibrary\MediaRepository */
30
    protected $mediaRepository;
31
32
    /** @var \Spatie\MediaLibrary\FileManipulator */
33
    protected $fileManipulator;
34
35
    /** @var \Illuminate\Contracts\Filesystem\Factory */
36
    protected $fileSystem;
37
38
    /** @var \Spatie\MediaLibrary\PathGenerator\BasePathGenerator */
39
    protected $basePathGenerator;
40
41
    /** @var bool */
42
    protected $isDryRun = false;
43
44
    /**
45
     * @param \Spatie\MediaLibrary\MediaRepository                 $mediaRepository
46
     * @param \Spatie\MediaLibrary\FileManipulator                 $fileManipulator
47
     * @param \Illuminate\Contracts\Filesystem\Factory             $fileSystem
48
     * @param \Spatie\MediaLibrary\PathGenerator\BasePathGenerator $basePathGenerator
49
     */
50
    public function __construct(
51
        MediaRepository $mediaRepository,
52
        FileManipulator $fileManipulator,
53
        Factory $fileSystem,
54
        BasePathGenerator $basePathGenerator
55
    ) {
56
        parent::__construct();
57
58
        $this->mediaRepository = $mediaRepository;
59
        $this->fileManipulator = $fileManipulator;
60
        $this->fileSystem = $fileSystem;
61
        $this->basePathGenerator = $basePathGenerator;
62
    }
63
64
    public function handle()
65
    {
66
        if (! $this->confirmToProceed()) {
67
            return;
68
        }
69
70
        $this->isDryRun = $this->option('dry-run');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->option('dry-run') of type string or array is incompatible with the declared type boolean of property $isDryRun.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
71
72
        $this->deleteFilesGeneratedForDeprecatedConversions();
73
74
        $this->deleteOrphanedDirectories();
75
76
        $this->info('All done!');
77
    }
78
79
    public function getMediaItems(): Collection
80
    {
81
        $modelType = $this->argument('modelType');
82
        $collectionName = $this->argument('collectionName');
83
84
        if (! is_null($modelType) && ! is_null($collectionName)) {
85
            return $this->mediaRepository->getByModelTypeAndCollectionName(
86
                $modelType,
0 ignored issues
show
Bug introduced by
It seems like $modelType defined by $this->argument('modelType') on line 81 can also be of type array; however, Spatie\MediaLibrary\Medi...TypeAndCollectionName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
87
                $collectionName
0 ignored issues
show
Bug introduced by
It seems like $collectionName defined by $this->argument('collectionName') on line 82 can also be of type array; however, Spatie\MediaLibrary\Medi...TypeAndCollectionName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
88
            );
89
        }
90
91
        if (! is_null($modelType)) {
92
            return $this->mediaRepository->getByModelType($modelType);
0 ignored issues
show
Bug introduced by
It seems like $modelType defined by $this->argument('modelType') on line 81 can also be of type array; however, Spatie\MediaLibrary\Medi...itory::getByModelType() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
93
        }
94
95
        if (! is_null($collectionName)) {
96
            return $this->mediaRepository->getByCollectionName($collectionName);
0 ignored issues
show
Bug introduced by
It seems like $collectionName defined by $this->argument('collectionName') on line 82 can also be of type array; however, Spatie\MediaLibrary\Medi...::getByCollectionName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
97
        }
98
99
        return $this->mediaRepository->all();
100
    }
101
102
    protected function deleteFilesGeneratedForDeprecatedConversions()
103
    {
104
        $this->getMediaItems()->each(function (Media $media) {
105
            $this->deleteConversionFilesForDeprecatedConversions($media);
106
            $this->deleteResponsiveImagesForDeprecatedConversions($media);
107
        });
108
    }
109
110
    protected function deleteConversionFilesForDeprecatedConversions(Media $media)
111
    {
112
        $conversionFilePaths = ConversionCollection::createForMedia($media)->getConversionsFiles($media->collection_name);
113
114
        $conversionPath = $this->basePathGenerator->getPathForConversions($media);
115
        $currentFilePaths = $this->fileSystem->disk($media->disk)->files($conversionPath);
116
117
        collect($currentFilePaths)
118
            ->reject(function (string $currentFilePath) use ($conversionFilePaths) {
119
                return $conversionFilePaths->contains(basename($currentFilePath));
120
            })
121
            ->each(function (string $currentFilePath) use ($media) {
122
                if (! $this->isDryRun) {
123
                    $this->fileSystem->disk($media->disk)->delete($currentFilePath);
124
                }
125
126
                $this->info("Deprecated conversion file `{$currentFilePath}` ".($this->isDryRun ? 'found' : 'has been removed'));
127
            });
128
    }
129
130
    protected function deleteResponsiveImagesForDeprecatedConversions(Media $media)
131
    {
132
        $conversionNames = ConversionCollection::createForMedia($media)
133
            ->map(function (Conversion $conversion) {
134
                return $conversion->getName();
135
            })
136
            ->push('medialibrary_original');
137
138
        $responsiveImagesGeneratedFor = array_keys($media->responsive_images);
139
140
        collect($responsiveImagesGeneratedFor)
141
            ->map(function (string $generatedFor) use ($media) {
142
                return $media->responsiveImages($generatedFor);
143
            })
144
            ->reject(function (RegisteredResponsiveImages $responsiveImages) use ($conversionNames) {
145
                return $conversionNames->contains($responsiveImages->generatedFor);
146
            })
147
            ->each(function (RegisteredResponsiveImages $responsiveImages) {
148
                if (! $this->isDryRun) {
149
                    $responsiveImages->delete();
150
                }
151
            });
152
    }
153
154
    protected function deleteOrphanedDirectories()
155
    {
156
        $diskName = $this->argument('disk') ?: config('medialibrary.default_filesystem');
157
158
        if (is_null(config("filesystems.disks.{$diskName}"))) {
159
            throw FileCannotBeAdded::diskDoesNotExist($diskName);
0 ignored issues
show
Bug introduced by
The method diskDoesNotExist() does not seem to exist on object<Spatie\MediaLibra...ions\FileCannotBeAdded>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
160
        }
161
162
        $mediaIds = collect($this->mediaRepository->all()->pluck('id')->toArray());
163
164
        collect($this->fileSystem->disk($diskName)->directories())
165
            ->filter(function (string $directory) {
166
                return is_numeric($directory);
167
            })
168
            ->reject(function (string $directory) use ($mediaIds) {
169
                return $mediaIds->contains((int) $directory);
170
            })->each(function (string $directory) use ($diskName) {
171
                if (! $this->isDryRun) {
172
                    $this->fileSystem->disk($diskName)->deleteDirectory($directory);
173
                }
174
175
                $this->info("Orphaned media directory `{$directory}` ".($this->isDryRun ? 'found' : 'has been removed'));
176
            });
177
    }
178
}
179