diff --git a/config/ssg.php b/config/ssg.php index e3436ab..99242fb 100644 --- a/config/ssg.php +++ b/config/ssg.php @@ -60,6 +60,10 @@ // ], + 'paginators' => [ + // + ], + /* |-------------------------------------------------------------------------- | Exclude URLs diff --git a/src/Commands/StaticSiteGenerate.php b/src/Commands/StaticSiteGenerate.php index a944a8d..49c2e56 100644 --- a/src/Commands/StaticSiteGenerate.php +++ b/src/Commands/StaticSiteGenerate.php @@ -22,7 +22,7 @@ class StaticSiteGenerate extends Command * * @var string */ - protected $signature = 'statamic:ssg:generate {--workers=}'; + protected $signature = 'statamic:ssg:generate {--workers=} {--url=*}'; /** * The console command description. @@ -59,6 +59,7 @@ public function handle() try { $this->generator ->workers($workers ?? 1) + ->explicitUrls($this->option('url')) ->generate(); } catch (GenerationFailedException $e) { $this->line($e->getConsoleMessage()); diff --git a/src/Generator.php b/src/Generator.php index 3584c4f..4d5e7cd 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -34,6 +34,7 @@ class Generator protected $config; protected $request; protected $after; + protected $explicitUrls = []; protected $extraUrls; protected $workers = 1; protected $taskResults; @@ -73,6 +74,13 @@ public function after($after) return $this; } + public function explicitUrls(array $urls = []) + { + $this->explicitUrls = $urls; + + return $this; + } + public function addUrls($closure) { $this->extraUrls[] = $closure; @@ -84,11 +92,32 @@ public function generate() Site::setCurrent(Site::default()->handle()); - $this - ->bindGlide() - ->clearDirectory() - ->createContentFiles() - ->createSymlinks() + $this->bindGlide(); + + if (empty($this->explicitUrls)) { + $this->clearDirectory() + ->createContentFiles(); + } else { + foreach ($this->explicitUrls as $url) { + try { + // Create the content file for this specific URL + $this->createContentFiles( + $this->page($url) + ); + + // If this page is the start of a paginated collection, generate all the paginated URLs too + if (array_key_exists($url, ($this->config['paginators'] ?? []))) { + $this->createContentFiles( + $this->paginatedEntries($url, ...$this->config['paginators'][$url]) + ); + } + } catch (GenerationFailedException $e) { + // When generating multiple URLs, we don't want to fail the entire process when one fails. + } + } + } + + $this->createSymlinks() ->copyFiles() ->outputSummary(); @@ -167,7 +196,7 @@ public function copyFiles() return $this; } - protected function createContentFiles() + protected function createContentFiles(\Illuminate\Support\Collection $pages = null) { $request = tap(Request::capture(), function ($request) { $request->setConfig($this->config); @@ -175,7 +204,7 @@ protected function createContentFiles() Cascade::withRequest($request); }); - $pages = $this->gatherContent(); + $pages = $pages ?? $this->gatherContent(); Partyline::line("Generating {$pages->count()} content files..."); @@ -221,12 +250,23 @@ protected function gatherContent() return $pages; } + protected function page(string $url, string $site = null): \Illuminate\Support\Collection + { + return collect([Entry::findByUri(Str::start($url, '/'), $site ?? 'default')]) + ->map(function ($content) { + return $this->createPage($content); + }) + ->filter + ->isGeneratable(); + } + protected function pages() { return collect() ->merge($this->routes()) ->merge($this->urls()) ->merge($this->entries()) + ->merge($this->paginatedEntries()) ->merge($this->terms()) ->merge($this->scopedTerms()) ->values() @@ -331,6 +371,37 @@ protected function entries() ->isGeneratable(); } + protected function paginatedEntries(string $url = null, string $collection = null, int $perPage = 10, string $pageName = 'page') + { + if ($url && $collection) { + $config = [ + $url => [ + 'collection' => $collection, + 'perPage' => $perPage, + 'pageName' => $pageName, + ] + ]; + } + + $paginators = $config ?? $this->config['paginators']; + + foreach ($paginators as $path => $config) { + $total = Entry::query() + ->where('collection', $config['collection']) + ->where('status', 'published') + ->count(); + + $pages = collect(range(1, ceil($total / ($config['perPage'] ?? 10)))) + ->map(fn ($pageNum) => implode('/', [$path, ($config['pageName'] ?? 'page'), $pageNum])) + ->map(function ($url) { + $url = Str::start($url, '/'); + return $this->createPage(new Route($url)); + }); + } + + return $pages; + } + protected function terms() { return Term::all()