Skip to content

Commit d17af1b

Browse files
authored
Allow customization of sitemap queries (#361)
* Act on bindable `Sitemap` instance, instead of running static functions. * Return types. * Extract query to Laravel-inspired `forPage($page, $perPage)` style method. * Add test coverage. * It's fine if we just run this test on newer versions for now.
1 parent 7ab18a7 commit d17af1b

File tree

4 files changed

+180
-31
lines changed

4 files changed

+180
-31
lines changed

src/Http/Controllers/SitemapController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ public function index()
1919
$content = Cache::remember(Sitemap::CACHE_KEY.'_index', $cacheUntil, function () {
2020
return view('seo-pro::sitemap_index', [
2121
'xml_header' => '<?xml version="1.0" encoding="UTF-8"?>',
22-
'sitemaps' => Sitemap::paginatedSitemaps(),
22+
'sitemaps' => app(Sitemap::class)->paginatedSitemaps(),
2323
])->render();
2424
});
2525
} else {
2626
$content = Cache::remember(Sitemap::CACHE_KEY, $cacheUntil, function () {
2727
return view('seo-pro::sitemap', [
2828
'xml_header' => '<?xml version="1.0" encoding="UTF-8"?>',
29-
'pages' => Sitemap::pages(),
29+
'pages' => app(Sitemap::class)->pages(),
3030
])->render();
3131
});
3232
}
@@ -45,7 +45,7 @@ public function show($page)
4545
$cacheKey = Sitemap::CACHE_KEY.'_'.$page;
4646

4747
$content = Cache::remember($cacheKey, $cacheUntil, function () use ($page) {
48-
abort_if(empty($pages = Sitemap::paginatedPages($page)), 404);
48+
abort_if(empty($pages = app(Sitemap::class)->paginatedPages($page)), 404);
4949

5050
return view('seo-pro::sitemap', [
5151
'xml_header' => '<?xml version="1.0" encoding="UTF-8"?>',

src/Sitemap/Sitemap.php

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Statamic\SeoPro\Sitemap;
44

5+
use Illuminate\Support\Collection as IlluminateCollection;
6+
use Illuminate\Support\LazyCollection;
57
use Statamic\Facades\Blink;
68
use Statamic\Facades\Collection;
79
use Statamic\Facades\Entry;
@@ -16,36 +18,32 @@ class Sitemap
1618

1719
const CACHE_KEY = 'seo-pro.sitemap';
1820

19-
public static function pages()
21+
public function pages(): array
2022
{
21-
$sitemap = new static;
22-
2323
return collect()
24-
->merge($sitemap->getPages($sitemap->publishedEntries()))
25-
->merge($sitemap->getPages($sitemap->publishedTerms()))
26-
->merge($sitemap->getPages($sitemap->publishedCollectionTerms()))
27-
->sortBy(function ($page) {
28-
return substr_count(rtrim($page->path(), '/'), '/');
29-
})
24+
->merge($this->publishedEntries())
25+
->merge($this->publishedTerms())
26+
->merge($this->publishedCollectionTerms())
27+
->pipe(fn ($pages) => $this->getPages($pages))
28+
->sortBy(fn ($page) => substr_count(rtrim($page->path(), '/'), '/'))
3029
->values()
3130
->map
32-
->toArray();
31+
->toArray()
32+
->all();
3333
}
3434

35-
public static function paginatedPages(int $page)
35+
public function paginatedPages(int $page): array
3636
{
37-
$sitemap = new static;
38-
3937
$perPage = config('statamic.seo-pro.sitemap.pagination.limit', 100);
4038
$offset = ($page - 1) * $perPage;
4139
$remaining = $perPage;
4240

43-
$pages = collect([]);
41+
$pages = collect();
4442

45-
$entryCount = $sitemap->publishedEntriesCount() - 1;
43+
$entryCount = $this->publishedEntriesCount() - 1;
4644

4745
if ($offset < $entryCount) {
48-
$entries = $sitemap->publishedEntriesQuery()->offset($offset)->limit($perPage)->get();
46+
$entries = $this->publishedEntriesForPage($page, $perPage);
4947

5048
if ($entries->count() < $remaining) {
5149
$remaining -= $entries->count();
@@ -58,8 +56,9 @@ public static function paginatedPages(int $page)
5856
$offset = max($offset - $entryCount, 0);
5957

6058
$pages = $pages->merge(
61-
collect($sitemap->publishedTerms())
62-
->merge($sitemap->publishedCollectionTerms())
59+
collect()
60+
->merge($this->publishedTerms())
61+
->merge($this->publishedCollectionTerms())
6362
->skip($offset)
6463
->take($remaining)
6564
);
@@ -69,18 +68,18 @@ public static function paginatedPages(int $page)
6968
return [];
7069
}
7170

72-
return $sitemap->getPages($pages)
71+
return $this
72+
->getPages($pages)
7373
->values()
7474
->map
75-
->toArray();
75+
->toArray()
76+
->all();
7677
}
7778

78-
public static function paginatedSitemaps()
79+
public function paginatedSitemaps(): array
7980
{
80-
$sitemap = new static;
81-
8281
// would be nice to make terms a count query rather than getting the count from the terms collection
83-
$count = $sitemap->publishedEntriesCount() + $sitemap->publishedTerms()->count() + $sitemap->publishedCollectionTerms()->count();
82+
$count = $this->publishedEntriesCount() + $this->publishedTerms()->count() + $this->publishedCollectionTerms()->count();
8483

8584
$sitemapCount = ceil($count / config('statamic.seo-pro.sitemap.pagination.limit', 100));
8685

@@ -112,7 +111,7 @@ protected function getPages($items)
112111
->filter();
113112
}
114113

115-
private function publishedEntriesQuery()
114+
protected function publishedEntriesQuery()
116115
{
117116
$collections = Collection::all()
118117
->map(function ($collection) {
@@ -131,12 +130,23 @@ private function publishedEntriesQuery()
131130
->orderBy('uri');
132131
}
133132

134-
protected function publishedEntries()
133+
protected function publishedEntries(): LazyCollection
135134
{
136135
return $this->publishedEntriesQuery()->lazy();
137136
}
138137

139-
protected function publishedEntriesCount()
138+
protected function publishedEntriesForPage(int $page, int $perPage): IlluminateCollection
139+
{
140+
$offset = ($page - 1) * $perPage;
141+
142+
return $this
143+
->publishedEntriesQuery()
144+
->offset($offset)
145+
->limit($perPage)
146+
->get();
147+
}
148+
149+
protected function publishedEntriesCount(): int
140150
{
141151
return $this->publishedEntriesQuery()->count();
142152
}

tests/SitemapTest.php

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
namespace Tests;
44

5+
use Illuminate\Support\Collection as IlluminateCollection;
6+
use Statamic\Console\Composer\Lock;
7+
use Statamic\Facades\Blink;
58
use Statamic\Facades\Collection;
69
use Statamic\Facades\Config;
710
use Statamic\Facades\Entry;
11+
use Statamic\SeoPro\Sitemap\Sitemap;
12+
use Statamic\Statamic;
813

914
class SitemapTest extends TestCase
1015
{
11-
public function setUp(): void
16+
protected function setUp(): void
1217
{
1318
parent::setUp();
1419

@@ -365,4 +370,137 @@ public function it_404s_on_invalid_pagination_urls()
365370
->get('/sitemap_a.xml')
366371
->assertNotFound();
367372
}
373+
374+
/** @test */
375+
public function it_can_use_custom_sitemap_queries()
376+
{
377+
// Hacky/temporary version compare, because `reorder()` method we're using
378+
// in CustomSitemap class below requires 5.29.0+, and we don't want to
379+
// increase minimum required Statamic version just for test setup
380+
if (version_compare(ltrim(Lock::file(__DIR__.'/../composer.lock')->getInstalledVersion('statamic/cms'), 'v'), '5.29.0', '<')) {
381+
$this->markTestSkipped();
382+
}
383+
384+
app()->bind(Sitemap::class, CustomSitemap::class);
385+
386+
config()->set('statamic.seo-pro.sitemap.pagination.enabled', true);
387+
config()->set('statamic.seo-pro.sitemap.pagination.limit', 5);
388+
389+
$this->assertNull(Blink::get('ran-custom-entries-query'));
390+
$this->assertNull(Blink::get('ran-custom-entries-for-page-query'));
391+
392+
$content = $this
393+
->get('/sitemap_1.xml')
394+
->assertOk()
395+
->assertHeader('Content-Type', 'text/xml; charset=UTF-8')
396+
->getContent();
397+
398+
$this->assertEquals(2, Blink::get('ran-custom-entries-query'));
399+
$this->assertEquals(1, Blink::get('ran-custom-entries-for-page-query'));
400+
$this->assertCount(5, $this->getPagesFromSitemapXml($content));
401+
402+
$today = now()->format('Y-m-d');
403+
404+
$expected = <<<"EOT"
405+
<?xml version="1.0" encoding="UTF-8"?>
406+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
407+
408+
<url>
409+
<loc>http://cool-runnings.com/articles</loc>
410+
<lastmod>2020-01-17</lastmod>
411+
<changefreq>monthly</changefreq>
412+
<priority>0.5</priority>
413+
</url>
414+
415+
<url>
416+
<loc>http://cool-runnings.com/dance</loc>
417+
<lastmod>$today</lastmod>
418+
<changefreq>monthly</changefreq>
419+
<priority>0.5</priority>
420+
</url>
421+
422+
<url>
423+
<loc>http://cool-runnings.com/magic</loc>
424+
<lastmod>$today</lastmod>
425+
<changefreq>monthly</changefreq>
426+
<priority>0.5</priority>
427+
</url>
428+
429+
<url>
430+
<loc>http://cool-runnings.com/nectar</loc>
431+
<lastmod>$today</lastmod>
432+
<changefreq>monthly</changefreq>
433+
<priority>0.5</priority>
434+
</url>
435+
436+
<url>
437+
<loc>http://cool-runnings.com/topics</loc>
438+
<lastmod>2020-01-20</lastmod>
439+
<changefreq>monthly</changefreq>
440+
<priority>0.5</priority>
441+
</url>
442+
443+
</urlset>
444+
445+
EOT;
446+
447+
$this->assertEquals($expected, $content);
448+
449+
$content = $this
450+
->get('/sitemap_2.xml')
451+
->assertOk()
452+
->assertHeader('Content-Type', 'text/xml; charset=UTF-8')
453+
->getContent();
454+
455+
$this->assertEquals(4, Blink::get('ran-custom-entries-query'));
456+
$this->assertEquals(2, Blink::get('ran-custom-entries-for-page-query'));
457+
$this->assertCount(2, $this->getPagesFromSitemapXml($content));
458+
459+
$today = now()->format('Y-m-d');
460+
461+
$expected = <<<'EOT'
462+
<?xml version="1.0" encoding="UTF-8"?>
463+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
464+
465+
<url>
466+
<loc>http://cool-runnings.com</loc>
467+
<lastmod>2020-11-24</lastmod>
468+
<changefreq>monthly</changefreq>
469+
<priority>0.5</priority>
470+
</url>
471+
472+
<url>
473+
<loc>http://cool-runnings.com/about</loc>
474+
<lastmod>2020-01-17</lastmod>
475+
<changefreq>monthly</changefreq>
476+
<priority>0.5</priority>
477+
</url>
478+
479+
</urlset>
480+
481+
EOT;
482+
483+
$this->assertEquals($expected, $content);
484+
}
485+
}
486+
487+
class CustomSitemap extends Sitemap
488+
{
489+
protected function publishedEntriesQuery()
490+
{
491+
// count how many times this is called
492+
Blink::increment('ran-custom-entries-query');
493+
494+
// reversing from default order, just so we can assert query has effect on output
495+
return parent::publishedEntriesQuery()->reorder('uri', 'desc');
496+
}
497+
498+
protected function publishedEntriesForPage(int $page, int $perPage): IlluminateCollection
499+
{
500+
// count how many times this is called
501+
Blink::increment('ran-custom-entries-for-page-query');
502+
503+
// also reverse items on individual pages, again so we can assert query has effect on output
504+
return parent::publishedEntriesForPage($page, $perPage)->reverse();
505+
}
368506
}

tests/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
abstract class TestCase extends \Orchestra\Testbench\TestCase
1313
{
1414
protected $siteFixturePath = __DIR__.'/Fixtures/site';
15+
protected $files;
1516

1617
protected function getPackageProviders($app)
1718
{

0 commit comments

Comments
 (0)