From 1803c4963192a83e2aba302350d198529db5ea50 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 6 Nov 2026 12:54:07 +0000 Subject: [PATCH] Allow the static warm job class to be configured Adds a static_caching.warm_job option used by background recaching and statamic:static:warm --queue, so apps can swap in a StaticWarmJob subclass (e.g. to change its uniqueness or add a delay). Paginated pages are now dispatched with the same job class as the page that found them. Co-Authored-By: Claude Opus 5.5 --- config/static_caching.php | 6 +++ src/Console/Commands/StaticWarm.php | 2 +- src/Console/Commands/StaticWarmJob.php | 2 +- src/StaticCaching/Cachers/AbstractCacher.php | 4 +- tests/Console/Commands/StaticWarmJobTest.php | 24 ++++++++++ tests/Console/Commands/StaticWarmTest.php | 46 +++++++++++++++++++ tests/StaticCaching/ApplicationCacherTest.php | 24 ++++++++++ 7 files changed, 105 insertions(+), 3 deletions(-) diff --git a/config/static_caching.php b/config/static_caching.php index 2af89331ea4..f3af2dc9039 100644 --- a/config/static_caching.php +++ b/config/static_caching.php @@ -168,6 +168,10 @@ | that will be used when warming the static cache and | optionally set the "--insecure" flag by default. | + | You may also define the job class used to warm each URL. It should + | extend Statamic\Console\Commands\StaticWarmJob, for example to + | change its uniqueness or add a delay. + | */ 'warm_queue' => env('STATAMIC_STATIC_WARM_QUEUE'), @@ -176,6 +180,8 @@ 'warm_insecure' => env('STATAMIC_STATIC_WARM_INSECURE', false), + 'warm_job' => null, + /* |-------------------------------------------------------------------------- | Background Re-cache diff --git a/src/Console/Commands/StaticWarm.php b/src/Console/Commands/StaticWarm.php index f703d451487..684b39ef3e8 100644 --- a/src/Console/Commands/StaticWarm.php +++ b/src/Console/Commands/StaticWarm.php @@ -99,7 +99,7 @@ private function warm(): void $jobClass = $this->option('uncached') ? StaticWarmUncachedJob::class - : StaticWarmJob::class; + : (config('statamic.static_caching.warm_job') ?? StaticWarmJob::class); foreach ($requests as $request) { $jobClass::dispatch($request, $this->clientConfig()) diff --git a/src/Console/Commands/StaticWarmJob.php b/src/Console/Commands/StaticWarmJob.php index 4dac473f6c3..c240d478afd 100644 --- a/src/Console/Commands/StaticWarmJob.php +++ b/src/Console/Commands/StaticWarmJob.php @@ -41,7 +41,7 @@ public function handle() "{$pageName}={$page}", ]); }) - ->each(fn (string $uri) => StaticWarmJob::dispatch( + ->each(fn (string $uri) => static::dispatch( new Request('GET', $uri, $this->request->getHeaders()), $this->clientConfig )); diff --git a/src/StaticCaching/Cachers/AbstractCacher.php b/src/StaticCaching/Cachers/AbstractCacher.php index c70cee050f3..a15b8e714ff 100644 --- a/src/StaticCaching/Cachers/AbstractCacher.php +++ b/src/StaticCaching/Cachers/AbstractCacher.php @@ -292,7 +292,9 @@ public function refreshUrl($url, $domain = null) $request = new GuzzleRequest('GET', $url); - StaticWarmJob::dispatch($request, []) + $job = config('statamic.static_caching.warm_job') ?? StaticWarmJob::class; + + $job::dispatch($request, []) ->onConnection(config('statamic.static_caching.warm_queue_connection') ?? config('queue.default')) ->onQueue(config('statamic.static_caching.warm_queue')); }); diff --git a/tests/Console/Commands/StaticWarmJobTest.php b/tests/Console/Commands/StaticWarmJobTest.php index f0740f81e12..854da245376 100644 --- a/tests/Console/Commands/StaticWarmJobTest.php +++ b/tests/Console/Commands/StaticWarmJobTest.php @@ -30,6 +30,26 @@ public function it_sends_a_get_request() $this->assertEquals('/about', $mock->getLastRequest()->getUri()->getPath()); } + #[Test] + public function it_dispatches_paginated_pages_with_the_same_job_class() + { + Queue::fake(); + + $mock = new MockHandler([ + (new Response(200))->withHeader('X-Statamic-Pagination', [ + 'current' => 1, + 'total' => 2, + 'name' => 'page', + ]), + ]); + + $job = new CustomPaginatedStaticWarmJob(new Request('GET', 'http://localhost/blog'), ['handler' => HandlerStack::create($mock)]); + + $job->handle(); + + Queue::assertPushed(CustomPaginatedStaticWarmJob::class, 2); + } + #[Test] public function it_sends_a_get_request_and_dispatches_static_warm_job_for_page_with_pagination() { @@ -173,3 +193,7 @@ public function parse($response) $this->assertSame([1, 3, 'pa,ge'], $parser->parse($folded)); } } + +class CustomPaginatedStaticWarmJob extends StaticWarmJob +{ +} diff --git a/tests/Console/Commands/StaticWarmTest.php b/tests/Console/Commands/StaticWarmTest.php index 2c42d0b0016..87d12937023 100644 --- a/tests/Console/Commands/StaticWarmTest.php +++ b/tests/Console/Commands/StaticWarmTest.php @@ -189,6 +189,48 @@ public function it_queues_the_requests() }); } + #[Test] + public function it_queues_the_requests_using_the_configured_warm_job() + { + config([ + 'statamic.static_caching.strategy' => 'half', + 'statamic.static_caching.warm_job' => CustomStaticWarmJob::class, + 'queue.default' => 'redis', + ]); + + Queue::fake(); + + $this->artisan('statamic:static:warm', ['--queue' => true]) + ->expectsOutputToContain('Adding 2 requests') + ->assertExitCode(0); + + Queue::assertCount(2); + Queue::assertPushed(CustomStaticWarmJob::class, function ($job) { + return $job->request->getUri()->getPath() === '/about'; + }); + Queue::assertPushed(CustomStaticWarmJob::class, function ($job) { + return $job->request->getUri()->getPath() === '/contact'; + }); + } + + #[Test] + public function it_queues_the_uncached_job_when_the_uncached_option_is_used_with_a_configured_warm_job() + { + config([ + 'statamic.static_caching.strategy' => 'half', + 'statamic.static_caching.warm_job' => CustomStaticWarmJob::class, + 'queue.default' => 'redis', + ]); + + Queue::fake(); + + $this->artisan('statamic:static:warm', ['--queue' => true, '--uncached' => true]) + ->assertExitCode(0); + + Queue::assertPushed(StaticWarmUncachedJob::class, 2); + Queue::assertNotPushed(CustomStaticWarmJob::class); + } + #[Test] public function it_queues_the_request_when_the_uncached_option_is_used() { @@ -362,3 +404,7 @@ private function makeCollection() ->template('default'); } } + +class CustomStaticWarmJob extends StaticWarmJob +{ +} diff --git a/tests/StaticCaching/ApplicationCacherTest.php b/tests/StaticCaching/ApplicationCacherTest.php index 62e19739af6..c79c6cedc8b 100644 --- a/tests/StaticCaching/ApplicationCacherTest.php +++ b/tests/StaticCaching/ApplicationCacherTest.php @@ -95,6 +95,26 @@ public static function cachedResponseProvider() ]; } + #[Test] + public function refreshing_urls_dispatches_the_configured_warm_job() + { + Queue::fake(); + + config(['statamic.static_caching.warm_job' => CustomStaticWarmJob::class]); + + $cache = app(Repository::class); + $cacher = new ApplicationCacher($cache, ['base_url' => 'http://example.com']); + + $request = Request::create('http://example.com/one', 'GET'); + $cacher->cachePage($request, response('one', 200)); + + $cacher->refreshUrls(['/one']); + + Queue::assertPushed(CustomStaticWarmJob::class, function ($job) { + return str_contains((string) $job->request->getUri(), '/one'); + }); + } + #[Test] public function refreshing_a_wildcard_warms_successful_urls_and_invalidates_error_urls() { @@ -398,3 +418,7 @@ public static function currentUrlProvider() ]; } } + +class CustomStaticWarmJob extends StaticWarmJob +{ +}