diff --git a/src/Console/Commands/StaticWarm.php b/src/Console/Commands/StaticWarm.php index 7737f9d380..82a1c05e65 100644 --- a/src/Console/Commands/StaticWarm.php +++ b/src/Console/Commands/StaticWarm.php @@ -76,8 +76,6 @@ public function handle() private function warm(): void { - $client = new Client($this->clientConfig()); - $this->output->newLine(); $this->line('Compiling URLs...'); @@ -97,7 +95,7 @@ private function warm(): void } else { $this->line('Visiting '.count($requests).' URLs...'); - $pool = new Pool($client, $requests, [ + $pool = new Pool($this->client(), $requests, [ 'concurrency' => $this->concurrency(), 'fulfilled' => [$this, 'outputSuccessLine'], 'rejected' => [$this, 'outputFailureLine'], @@ -109,6 +107,27 @@ private function warm(): void } } + private function warmPaginatedPages(string $url, int $currentPage, int $totalPages, string $pageName): void + { + $urls = collect(range($currentPage, $totalPages))->map(function ($page) use ($url, $pageName) { + return "{$url}?{$pageName}={$page}"; + }); + + $requests = $urls->map(fn (string $url) => new Request('GET', $url))->all(); + + $pool = new Pool($this->client(), $requests, [ + 'concurrency' => $this->concurrency(), + 'fulfilled' => function (Response $response, $index) use ($urls) { + $this->components->twoColumnDetail($this->getRelativeUri($urls->get($index)), '✓ Cached'); + }, + 'rejected' => [$this, 'outputFailureLine'], + ]); + + $promise = $pool->promise(); + + $promise->wait(); + } + private function concurrency(): int { $strategy = config('statamic.static_caching.strategy'); @@ -116,6 +135,11 @@ private function concurrency(): int return config("statamic.static_caching.strategies.$strategy.warm_concurrency", 25); } + private function client(): Client + { + return new Client($this->clientConfig()); + } + private function clientConfig(): array { return [ @@ -128,12 +152,18 @@ private function clientConfig(): array public function outputSuccessLine(Response $response, $index): void { - $this->components->twoColumnDetail($this->getRelativeUri($index), '✓ Cached'); + $this->components->twoColumnDetail($this->getRelativeUri($this->uris()->get($index)), '✓ Cached'); + + if ($response->hasHeader('X-Statamic-Pagination')) { + [$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination'); + + $this->warmPaginatedPages($this->uris()->get($index), $currentPage, $totalPages, $pageName); + } } public function outputFailureLine($exception, $index): void { - $uri = $this->getRelativeUri($index); + $uri = $this->getRelativeUri($this->uris()->get($index)); if ($exception instanceof RequestException && $exception->hasResponse()) { $response = $exception->getResponse(); @@ -150,9 +180,9 @@ public function outputFailureLine($exception, $index): void $this->components->twoColumnDetail($uri, "$message"); } - private function getRelativeUri(int $index): string + private function getRelativeUri(string $uri): string { - return Str::start(Str::after($this->uris()->get($index), config('app.url')), '/'); + return Str::start(Str::after($uri, config('app.url')), '/'); } private function requests() diff --git a/src/Console/Commands/StaticWarmJob.php b/src/Console/Commands/StaticWarmJob.php index 4ae92b1b42..09de9b93aa 100644 --- a/src/Console/Commands/StaticWarmJob.php +++ b/src/Console/Commands/StaticWarmJob.php @@ -24,6 +24,18 @@ public function __construct(public Request $request, public array $clientConfig) public function handle() { - (new Client($this->clientConfig))->send($this->request); + $response = (new Client($this->clientConfig))->send($this->request); + + if ($response->hasHeader('X-Statamic-Pagination')) { + [$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination'); + + collect(range($currentPage, $totalPages)) + ->map(function (int $page) use ($pageName) { + return "{$this->request->getUri()}?{$pageName}={$page}"; + }) + ->each(function (string $uri) { + StaticWarmJob::dispatch(new Request('GET', $uri), $this->clientConfig); + }); + } } } diff --git a/src/StaticCaching/Cachers/FileCacher.php b/src/StaticCaching/Cachers/FileCacher.php index 7be32839a5..8e653d300d 100644 --- a/src/StaticCaching/Cachers/FileCacher.php +++ b/src/StaticCaching/Cachers/FileCacher.php @@ -167,7 +167,17 @@ public function getFilePath($url, $site = null) $urlParts = parse_url($url); $pathParts = pathinfo($urlParts['path']); $slug = $pathParts['basename']; - $query = $this->config('ignore_query_strings') ? '' : Arr::get($urlParts, 'query', ''); + $query = Arr::get($urlParts, 'query', ''); + + if ($this->config('ignore_query_strings')) { + $allowedQueryParams = collect($this->config('allowed_query_strings', [])) + ->map(fn ($param) => Str::ensureRight($param, '=')) + ->all(); + + $query = collect(explode('&', $query)) + ->filter(fn ($param) => Str::startsWith($param, $allowedQueryParams)) + ->implode('&'); + } if ($this->isBasenameTooLong($basename = $slug.'_'.$query.'.html')) { $basename = $slug.'_lqs_'.md5($query).'.html'; diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index 3312ddab49..0777e876e1 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -10,6 +10,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache as AppCache; use Illuminate\Support\Facades\Log; +use Statamic\Facades\Blink; use Statamic\Facades\StaticCache; use Statamic\Statamic; use Statamic\StaticCaching\Cacher; @@ -75,6 +76,16 @@ private function handleRequest($request, Closure $next) $this->makeReplacementsAndCacheResponse($request, $response); $this->nocache->write(); + + if ($paginator = Blink::get('tag-paginator')) { + if ($paginator->hasMorePages()) { + $response->headers->set('X-Statamic-Pagination', [ + 'current' => $paginator->currentPage(), + 'total' => $paginator->lastPage(), + 'name' => $paginator->getPageName(), + ]); + } + } } elseif (! $response->isRedirect()) { $this->makeReplacements($response); } diff --git a/tests/Console/Commands/StaticWarmJobTest.php b/tests/Console/Commands/StaticWarmJobTest.php index b92c0e2941..8baa3247fd 100644 --- a/tests/Console/Commands/StaticWarmJobTest.php +++ b/tests/Console/Commands/StaticWarmJobTest.php @@ -6,6 +6,7 @@ use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use Illuminate\Support\Facades\Queue; use PHPUnit\Framework\Attributes\Test; use Statamic\Console\Commands\StaticWarmJob; use Tests\TestCase; @@ -27,4 +28,41 @@ public function it_sends_a_get_request() $this->assertEquals('/about', $mock->getLastRequest()->getUri()->getPath()); } + + #[Test] + public function it_sends_a_get_request_and_dispatches_static_warm_job_for_page_with_pagination() + { + Queue::fake(); + + $mock = new MockHandler([ + (new Response(200))->withHeader('X-Statamic-Pagination', [ + 'current' => 1, + 'total' => 3, + 'name' => 'page', + ]), + ]); + + $handlerStack = HandlerStack::create($mock); + + $job = new StaticWarmJob(new Request('GET', '/blog'), ['handler' => $handlerStack]); + + $job->handle(); + + $this->assertEquals('/blog', $mock->getLastRequest()->getUri()->getPath()); + + Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) { + return $job->request->getUri()->getPath() === '/blog' + && $job->request->getUri()->getQuery() === 'page=1'; + }); + + Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) { + return $job->request->getUri()->getPath() === '/blog' + && $job->request->getUri()->getQuery() === 'page=2'; + }); + + Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) { + return $job->request->getUri()->getPath() === '/blog' + && $job->request->getUri()->getQuery() === 'page=3'; + }); + } }