diff --git a/src/Imaging/ImageGenerator.php b/src/Imaging/ImageGenerator.php index c419010fc51..71265237941 100644 --- a/src/Imaging/ImageGenerator.php +++ b/src/Imaging/ImageGenerator.php @@ -2,6 +2,7 @@ namespace Statamic\Imaging; +use Closure; use Facades\Statamic\Imaging\ImageValidator; use Illuminate\Support\Facades\Storage; use League\Flysystem\Filesystem; @@ -82,7 +83,7 @@ public function setParams(array $params) */ public function generateByPath($path, array $params) { - return Glide::cacheStore()->rememberForever( + return $this->findOrGenerate( 'path::'.$path.'::'.md5(json_encode($params)), fn () => $this->doGenerateByPath($path, $params) ); @@ -108,7 +109,7 @@ private function doGenerateByPath($path, array $params, $sourceFilesystemRoot = */ public function generateByUrl($url, array $params) { - return Glide::cacheStore()->rememberForever( + return $this->findOrGenerate( 'url::'.$url.'::'.md5(json_encode($params)), fn () => $this->doGenerateByUrl($url, $params) ); @@ -168,7 +169,7 @@ public function generateByAsset($asset, array $params) collect(Glide::cacheStore()->get($manifestCacheKey, []))->push($manipulationCacheKey)->unique()->all() ); - return Glide::cacheStore()->rememberForever( + return $this->findOrGenerate( $manipulationCacheKey, fn () => $this->doGenerateByAsset($asset, $params) ); @@ -190,6 +191,19 @@ private function doGenerateByAsset($asset, array $params) return $this->generate($this->asset->basename()); } + private function findOrGenerate(string $cacheKey, Closure $callback) + { + $store = Glide::cacheStore(); + + if (($path = $store->get($cacheKey)) && $this->server->getCache()->fileExists($path)) { + return $path; + } + + $store->forget($cacheKey); + + return $store->rememberForever($cacheKey, $callback); + } + public static function assetCacheManifestKey($asset) { return 'asset::'.$asset->id(); diff --git a/tests/Imaging/ImageGeneratorTest.php b/tests/Imaging/ImageGeneratorTest.php index f774a2af27b..bf840fdbbef 100644 --- a/tests/Imaging/ImageGeneratorTest.php +++ b/tests/Imaging/ImageGeneratorTest.php @@ -98,6 +98,35 @@ public function it_generates_an_image_by_asset() Event::assertDispatchedTimes(GlideImageGenerated::class, 1); } + #[Test] + public function it_regenerates_an_image_by_asset_when_the_cached_file_is_missing() + { + Event::fake(); + + Storage::fake('test'); + $file = UploadedFile::fake()->image('foo/hoff.jpg', 30, 60); + Storage::disk('test')->putFileAs('foo', $file, 'hoff.jpg'); + $container = tap(AssetContainer::make('test_container')->disk('test'))->save(); + $asset = tap($container->makeAsset('foo/hoff.jpg'))->save(); + + ImageValidator::shouldReceive('isValidImage') + ->andReturnTrue() + ->times(2); // Two manipulations should happen because the cached file gets deleted. + + $path = $this->makeGenerator()->generateByAsset($asset, ['w' => 100, 'h' => 100]); + + // Delete the generated file, but keep the cache store entry pointing to it. + Glide::cacheDisk()->delete($path); + $this->assertCount(0, $this->generatedImagePaths()); + + $regeneratedPath = $this->makeGenerator()->generateByAsset($asset, ['w' => 100, 'h' => 100]); + + $this->assertEquals($path, $regeneratedPath); + $this->assertCount(1, $paths = $this->generatedImagePaths()); + $this->assertContains($path, $paths); + Event::assertDispatchedTimes(GlideImageGenerated::class, 2); + } + #[Test] public function it_throws_unable_to_read_file_when_asset_is_not_a_valid_image() {