Skip to content

Instantly share code, notes, and snippets.

@jasonvarga
Last active June 26, 2023 22:10
Show Gist options
  • Save jasonvarga/256f293f8f55bf564c907a335a2f40f3 to your computer and use it in GitHub Desktop.
Save jasonvarga/256f293f8f55bf564c907a335a2f40f3 to your computer and use it in GitHub Desktop.
Statamic SSG Pagination

Statamic SSG Pagination

We will likely introduce better native support for pagination at some point, but for now, this is an okay workaround that only requires a little code.

This will change ?page=2 pagination URLs to /page/2 URLs, so the SSG is able to recognize them as their own pages.

Paginator

Copy UrlPaginator into app/UrlPaginator.php.

You can put it somewhere else, but be sure to update the namespace in the class itself, and any references to it.

This class overrides Statamic's paginator and changes the /url?page=2 urls to /url/page/2 urls.

Service Provider

Copy the contents of the boot method into your own AppServiceProvider.

In here it will:

  • Swap out Statamic's paginator for the url based one
  • Tell the paginator to look at the /page/{page} part of the url for the page number, rather than looking for a ?page= query string.
  • Add any paginator urls to the SSG's "urls" config.

Add routes

Since this method of pagination means there will be separate pages, you'll need a route.

You can copy the route in web.php and tweak as necessary. Make sure to keep page/{page} in there.

Rinse and repeat for any other sections of your site that have pagination.

Adjust your URL logic

In the service provider, you'll see this block:

$config['urls'] = array_merge(
    $config['urls'],
    $this->articleUrls(),
    // $this->blogUrls(),
    // $this->tagUrls(),
    // etc
);

This is going to dynamically add URLs to the ssg.php config file.

  • Copy over the articleUrls method.
  • Tweak the naming for what makes sense for you.
  • Adjust the URL, query, and perPage in order to correspond to your collection tag.
  • Repeat for any other sections of your site that have pagination.
<?php
namespace App\Providers;
use App\UrlPaginator;
use Illuminate\Support\ServiceProvider;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Entry;
use Statamic\StaticSite\Generator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->bootSsg();
}
}
private function bootSsg()
{
$this->app->extend(LengthAwarePaginator::class, function ($paginator) {
$options = $paginator->getOptions();
$options['path'] = preg_replace('/\/page\/\d+$/', '', $options['path']);
return $this->app->makeWith(UrlPaginator::class, [
'items' => $paginator->getCollection(),
'total' => $paginator->total(),
'perPage' => $paginator->perPage(),
'currentPage' => $paginator->currentPage(),
'options' => $options,
]);
});
UrlPaginator::currentPageResolver(function () {
return optional($this->app['request']->route())->parameter('page');
});
$this->app->beforeResolving(Generator::class, function ($generator) {
$config = config('statamic.ssg');
$config['urls'] = array_merge(
$config['urls'],
$this->articleUrls(),
// $this->blogUrls(),
// $this->tagUrls(),
// etc
);
config(['statamic.ssg' => $config]);
});
}
private function articleUrls()
{
// The URL of the listing.
$url = '/articles';
// The number of entries per page, according to your collection tag.
$perPage = 10;
// The total number of entries in the collection.
// Make sure to mimic whatever params/filters are on the collection tag.
$total = Entry::query()->where('collection', 'articles')->where('status', 'published')->count();
return collect(range(1, ceil($total / $perPage)))
->map(fn ($page) => $url.'/page/'.$page)
->all();
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}
<?php
namespace App;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
class UrlPaginator extends LengthAwarePaginator
{
public function url($page)
{
if ($page <= 0) {
$page = 1;
}
$url = $this->path().'/'.$this->pageName.'/'.$page;
if (Str::contains($this->path(), '?') || count($this->query)) {
$url .= '?'.Arr::query($this->query);
}
return $url.$this->buildFragment();
}
}
<?php
Route::statamic('articles/page/{page}', 'articles.index', ['load' => 'id-of-articles-page']);
@simonhamp
Copy link

Note that you may bump into issues when deploying a site in a fresh environment if you're using a database with this logic as the service provider will try to access the database, which you generally shouldn't do in a service provider.

This is because you'll bump into a 'database accessed before migration' issue which may present in slightly horrible fashion.

I got around this by using a simple flag in my .env file:

    public function boot()
    {
-        if ($this->app->runningInConsole()) {
+        if (env('SSG_ENABLED')) {
            $this->bootSsg();
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment