Completed
Push — master ( 3d3ccc...56c03c )
by Freek
03:52
created

CleanCommand::markConversionAsRemoved()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 2
1
<?php
2
3
namespace Spatie\MediaLibrary\Commands;
4
5
use Illuminate\Console\Command;
6
use Spatie\MediaLibrary\Models\Media;
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\RegisteredResponsiveImages;
17
18
class CleanCommand extends Command
19
{
20
    use ConfirmableTrait;
21
22
    protected $signature = 'medialibrary:clean {modelType?} {collectionName?} {disk?}
23
    {--dry-run : List files that will be removed without removing them},
24
    {--force : Force the operation to run when in production}';
25
26
    protected $description = 'Clean deprecated conversions and files without related model.';
27
28
    /** @var \Spatie\MediaLibrary\MediaRepository */
29
    protected $mediaRepository;
30
31
    /** @var \Spatie\MediaLibrary\FileManipulator */
32
    protected $fileManipulator;
33
34
    /** @var \Illuminate\Contracts\Filesystem\Factory */
35
    protected $fileSystem;
36
37
    /** @var \Spatie\MediaLibrary\PathGenerator\BasePathGenerator */
38
    protected $basePathGenerator;
39
40
    /** @var bool */
41
    protected $isDryRun = false;
42
43
    /**
44
     * @param \Spatie\MediaLibrary\MediaRepository                 $mediaRepository
45
     * @param \Spatie\MediaLibrary\FileManipulator                 $fileManipulator
46
     * @param \Illuminate\Contracts\Filesystem\Factory             $fileSystem
47
     * @param \Spatie\MediaLibrary\PathGenerator\BasePathGenerator $basePathGenerator
48
     */
49
    public function __construct(
50
        MediaRepository $mediaRepository,
51
        FileManipulator $fileManipulator,
52
        Factory $fileSystem,
53
        BasePathGenerator $basePathGenerator
54
    ) {
55
        parent::__construct();
56
57
        $this->mediaRepository = $mediaRepository;
58
        $this->fileManipulator = $fileManipulator;
59
        $this->fileSystem = $fileSystem;
60
        $this->basePathGenerator = $basePathGenerator;
61
    }
62
63
    public function handle()
64
    {
65
        if (! $this->confirmToProceed()) {
66
            return;
67
        }
68
69
        $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...
70
71
        $this->deleteFilesGeneratedForDeprecatedConversions();
72
73
        $this->deleteOrphanedDirectories();
74
75
        $this->info('All done!');
76
    }
77
78
    public function getMediaItems(): Collection
79
    {
80
        $modelType = $this->argument('modelType');
81
        $collectionName = $this->argument('collectionName');
82
83
        if (! is_null($modelType) && ! is_null($collectionName)) {
84
            return $this->mediaRepository->getByModelTypeAndCollectionName(
85
                $modelType,
0 ignored issues
show
Bug introduced by
It seems like $modelType defined by $this->argument('modelType') on line 80 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...
86
                $collectionName
0 ignored issues
show
Bug introduced by
It seems like $collectionName defined by $this->argument('collectionName') 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
            );
88
        }
89
90
        if (! is_null($modelType)) {
91
            return $this->mediaRepository->getByModelType($modelType);
0 ignored issues
show
Bug introduced by
It seems like $modelType defined by $this->argument('modelType') on line 80 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...
92
        }
93
94
        if (! is_null($collectionName)) {
95
            return $this->mediaRepository->getByCollectionName($collectionName);
0 ignored issues
show
Bug introduced by
It seems like $collectionName defined by $this->argument('collectionName') on line 81 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...
96
        }
97
98
        return $this->mediaRepository->all();
99
    }
100
101
    protected function deleteFilesGeneratedForDeprecatedConversions()
102
    {
103
        $this->getMediaItems()->each(function (Media $media) {
104
            $this->deleteConversionFilesForDeprecatedConversions($media);
105
            $this->deleteResponsiveImagesForDeprecatedConversions($media);
106
        });
107
    }
108
109
    protected function deleteConversionFilesForDeprecatedConversions(Media $media)
110
    {
111
        $conversionFilePaths = ConversionCollection::createForMedia($media)->getConversionsFiles($media->collection_name);
112
113
        $conversionPath = $this->basePathGenerator->getPathForConversions($media);
114
        $currentFilePaths = $this->fileSystem->disk($media->disk)->files($conversionPath);
115
116
        collect($currentFilePaths)
117
            ->reject(function (string $currentFilePath) use ($conversionFilePaths) {
118
                return $conversionFilePaths->contains(basename($currentFilePath));
119
            })
120
            ->each(function (string $currentFilePath) use ($media) {
121
                if (! $this->isDryRun) {
122
                    $this->fileSystem->disk($media->disk)->delete($currentFilePath);
123
124
                    $this->markConversionAsRemoved($media, $currentFilePath);
125
                }
126
127
                $this->info("Deprecated conversion file `{$currentFilePath}` ".($this->isDryRun ? 'found' : 'has been removed'));
128
            });
129
    }
130
131
    protected function deleteResponsiveImagesForDeprecatedConversions(Media $media)
132
    {
133
        $conversionNames = ConversionCollection::createForMedia($media)
134
            ->map(function (Conversion $conversion) {
135
                return $conversion->getName();
136
            })
137
            ->push('medialibrary_original');
138
139
        $responsiveImagesGeneratedFor = array_keys($media->responsive_images);
140
141
        collect($responsiveImagesGeneratedFor)
142
            ->map(function (string $generatedFor) use ($media) {
143
                return $media->responsiveImages($generatedFor);
144
            })
145
            ->reject(function (RegisteredResponsiveImages $responsiveImages) use ($conversionNames) {
146
                return $conversionNames->contains($responsiveImages->generatedFor);
147
            })
148
            ->each(function (RegisteredResponsiveImages $responsiveImages) {
149
                if (! $this->isDryRun) {
150
                    $responsiveImages->delete();
151
                }
152
            });
153
    }
154
155
    protected function deleteOrphanedDirectories()
156
    {
157
        $diskName = $this->argument('disk') ?: config('medialibrary.disk_name');
158
159
        if (is_null(config("filesystems.disks.{$diskName}"))) {
160
            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...
161
        }
162
163
        $mediaIds = collect($this->mediaRepository->all()->pluck('id')->toArray());
164
165
        collect($this->fileSystem->disk($diskName)->directories())
166
            ->filter(function (string $directory) {
167
                return is_numeric($directory);
168
            })
169
            ->reject(function (string $directory) use ($mediaIds) {
170
                return $mediaIds->contains((int) $directory);
171
            })->each(function (string $directory) use ($diskName) {
172
                if (! $this->isDryRun) {
173
                    $this->fileSystem->disk($diskName)->deleteDirectory($directory);
174
                }
175
176
                $this->info("Orphaned media directory `{$directory}` ".($this->isDryRun ? 'found' : 'has been removed'));
177
            });
178
    }
179
180
    protected function markConversionAsRemoved(Media $media, string $conversionPath)
181
    {
182
        $conversionFile = pathinfo($conversionPath, PATHINFO_FILENAME);
183
184
        $generatedConversionName = null;
0 ignored issues
show
Unused Code introduced by
$generatedConversionName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
185
186
        $media->getGeneratedConversions()
187
            ->filter(function (bool $isGenerated, string $generatedConversionName) use ($conversionFile) {
188
                return str_contains($conversionFile, $generatedConversionName);
189
            })
190
            ->each(function (bool $isGenerated, string $generatedConversionName) use ($media) {
191
                $media->markAsConversionGenerated($generatedConversionName, false);
192
            });
193
194
        $media->save();
195
    }
196
}
197