Collections
In this project, collections are defined dynamically from an environment-driven docs root.
Instead of declaring collections inline in config.php, we load them from source/_core/collections.php, which scans language folders under DOCS_DIR.
How it works
-
You set the docs root in
.env:DOCS_DIR=docs -
The loader reads
source/<DOCS_DIR>/*(e.g.source/docs/en,source/docs/ru) and creates one collection per language. -
Each collection is named as
<docs-dir-with-dashes>-<lang>, for example:docs-endocs-ru
-
The output paths are generated to keep Jigsaw's pretty URLs:
.../index.md->/en/index.html,/en/start/index.html, ....../page.md->/en/.../page/index.html
Enabling the collections
config.php
<?php
return [
// ...
'collections' => require_once 'source/_core/collections.php',
];
The collections loader
source/_core/collections.php
<?php
use Illuminate\Support\Str;
$collections = [];
// Turn "docs" or "docs/content" into "docs" or "docs-content"
$collectionName = collect(explode('/', trim(str_replace('\\', '/', $_ENV['DOCS_DIR']), '/')))
->implode('-');
// Scan language folders: source/<DOCS_DIR>/* -> en, ru, ...
foreach (glob('./source/' . $_ENV['DOCS_DIR'] . '/*', GLOB_ONLYDIR) as $dir) {
$lang = basename($dir);
$collections["{$collectionName}-{$lang}"] = [
// point to "docs" (the directory under source/)
'directory' => basename('/source/' . $_ENV['DOCS_DIR']),
'language' => $lang,
'extends' => '_core._layouts.documentation',
// only .md pages
'filter' => fn ($page) => $page->_meta->extension === 'md',
// build pretty, language-prefixed paths
'path' => function ($page) use ($lang) {
$relative = trim(str_replace('\\', '/', $page->_meta->relativePath), '/');
// index.md (root or nested) -> no explicit "/index" here;
// PrettyOutputPathResolver will add index.html.
if ($page->_meta->filename === 'index') {
return $lang . ($relative ? '/' . $relative : '');
}
// other pages -> /<lang>/<relative>/<filename>
return $lang . ($relative ? '/' . $relative : '') . '/' . $page->_meta->filename;
},
];
}
return $collections;
Example structure
!folders
- source
- {$DOCS_DIR}
- en
-- index.md
- start -- index.md -- install.md
- local-development -- index.md
- ru -- index.md -- ... (per-language sections) !endfolders
- en
-- index.md
- {$DOCS_DIR}
Using collections in Blade
You can iterate over a language collection to build navigation, lists, etc.
Collection variables follow the generated names (e.g. docs-en, docs-ru). If a hyphenated name is inconvenient in Blade, access through the page data:
@php
$docsEn = $page->collections['docs-en'] ?? collect();
@endphp
<ul>
@foreach ($docsEn as $doc)
<li><a href="{{ $doc->getPath() }}">{{ $doc->title ?? $doc->getFilename() }}</a></li>
@endforeach
</ul>
Tip: front matter like
title,nav_order, etc., can be defined per page and used for sorting or building menus.
Notes & gotchas
- Keep folder names URL-friendly (no spaces); prefer
local-developmentoverLocal Development. - For root
index.mdin each language, the path function must return only the language segment (en,ru) - the resolver will writeindex.htmlautomatically. - Clear your build/cache if paths change:
rm -rf build_local/ .cache/
That's it - this mirrors your current setup while staying consistent with Jigsaw's conventions.