Skip to content

Commit 042ff27

Browse files
committed
Optimize sitemap performance
1 parent efc8b2b commit 042ff27

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

src/Controller/SitemapController.php

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace SpeedPuzzling\Web\Controller;
66

7-
use SpeedPuzzling\Web\Query\GetPuzzlesOverview;
7+
use SpeedPuzzling\Web\Query\GetPuzzleIdsForSitemap;
88
use SpeedPuzzling\Web\Value\CountryCode;
99
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1010
use Symfony\Component\HttpFoundation\Response;
@@ -13,8 +13,10 @@
1313

1414
final class SitemapController extends AbstractController
1515
{
16+
private const array LOCALES = ['cs', 'en', 'es', 'ja', 'fr', 'de'];
17+
1618
public function __construct(
17-
readonly private GetPuzzlesOverview $getPuzzlesOverview,
19+
readonly private GetPuzzleIdsForSitemap $getPuzzleIdsForSitemap,
1820
) {
1921
}
2022

@@ -31,20 +33,25 @@ public function __invoke(): Response
3133
$staticRoutes = ['homepage', 'contact', 'faq', 'ladder', 'ladder_solo_500_pieces', 'ladder_solo_1000_pieces', 'ladder_pairs_500_pieces', 'ladder_pairs_1000_pieces', 'ladder_groups_500_pieces', 'ladder_groups_1000_pieces', 'privacy_policy', 'puzzles', 'players', 'recent_activity', 'terms_of_service', 'hub', 'events'];
3234

3335
foreach ($staticRoutes as $route) {
34-
foreach (['cs', 'en', 'es', 'ja', 'fr', 'de'] as $locale) {
36+
foreach (self::LOCALES as $locale) {
3537
$urls[$route][$locale] = $this->generateUrl($route, ['_locale' => $locale], UrlGeneratorInterface::ABSOLUTE_URL);
3638
}
3739
}
3840

39-
foreach (['cs', 'en', 'es', 'ja', 'fr', 'de'] as $locale) {
40-
foreach ($this->getPuzzlesOverview->allApprovedOrAddedByPlayer(null) as $puzzles) {
41-
$urls[$puzzles->puzzleId][$locale] = $this->generateUrl('puzzle_detail', [
41+
// Fetch puzzle IDs once (not inside locale loop!)
42+
$puzzleIds = $this->getPuzzleIdsForSitemap->allApproved();
43+
44+
foreach ($puzzleIds as $puzzleId) {
45+
foreach (self::LOCALES as $locale) {
46+
$urls[$puzzleId][$locale] = $this->generateUrl('puzzle_detail', [
4247
'_locale' => $locale,
43-
'puzzleId' => $puzzles->puzzleId,
48+
'puzzleId' => $puzzleId,
4449
], UrlGeneratorInterface::ABSOLUTE_URL);
4550
}
51+
}
4652

47-
foreach (CountryCode::cases() as $countryCode) {
53+
foreach (CountryCode::cases() as $countryCode) {
54+
foreach (self::LOCALES as $locale) {
4855
$urls['players_per_country_' . $countryCode->name][$locale] = $this->generateUrl('players_per_country', [
4956
'_locale' => $locale,
5057
'countryCode' => $countryCode->name,
@@ -53,9 +60,13 @@ public function __invoke(): Response
5360
}
5461

5562
$response = new Response(headers: [
56-
'Content-Type', 'text/xml',
63+
'Content-Type' => 'text/xml',
5764
]);
5865

66+
// Cache for 6 hours - sitemap doesn't need real-time updates
67+
$response->setSharedMaxAge(21600);
68+
$response->setMaxAge(3600);
69+
5970
return $this->render('sitemap.xml.twig', [
6071
'urls' => $urls,
6172
], $response);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SpeedPuzzling\Web\Query;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
readonly final class GetPuzzleIdsForSitemap
10+
{
11+
public function __construct(
12+
private Connection $database,
13+
) {
14+
}
15+
16+
/**
17+
* @return array<string>
18+
*/
19+
public function allApproved(): array
20+
{
21+
$query = <<<SQL
22+
SELECT puzzle.id
23+
FROM puzzle
24+
WHERE puzzle.approved = true
25+
SQL;
26+
27+
/** @var array<string> $puzzleIds */
28+
$puzzleIds = $this->database
29+
->executeQuery($query)
30+
->fetchFirstColumn();
31+
32+
return $puzzleIds;
33+
}
34+
}

0 commit comments

Comments
 (0)