Virtual Pages
Render pages backed by external JSON or API data through the normal routing and template pipeline.
What a virtual page is
A Page backed by data that was never read from disk, for exposing an external API (events, products, any JSON) as a renderable page.
A virtual page is never injected globally. It exists only because a real page, with a real folder and index.json, routes to it explicitly, the same constraint that governs addons. PhlatPage stays a flat-file CMS first; API integration sits on top.
Creating one
App::page(string $uri, array $data) already supports this. A non-empty $data skips every disk read, and a view in $data still resolves the right view-bound Page subclass:
$app->page('events/summer-fest', $syntheticData);
The result is a normal Page, with the same class and template resolution as a real one.
In a router
A real page's router builds the data and returns the page. Handlers may return a different Page:
// site/views/events/router.php, for the real page at data/pages/events/
use Phlat\App;
use Phlat\Page;
use Phlat\Exceptions\NotFoundException;
use Phlat\Toolkit\Hash;
return function (App $app, Page $page): void {
$page->router->get('{slug}', function ($slug) use ($app, $page) {
$record = (new EventsSource())->find($slug);
if (!$record) throw new NotFoundException();
return $app->page("{$page->url()}/{$slug}", [
'uuid' => Hash::sha256("events:{$slug}"),
'view' => 'event',
'data' => $record,
]);
});
};
Use a conventional URI like "{$page->url()}/{$slug}" and url(), template resolution and everything else derived from the path just work.
Listing
For an index, build each page the same way and collect them in a PageCollection, which accepts appended instances:
$events = new PageCollection($app);
foreach ($source->all() as $slug => $record) {
$events->append($app->page("{$page->url()}/{$slug}", [
'uuid' => Hash::sha256("events:{$slug}"),
'view' => 'event',
'data' => $record,
]));
}
Deliberately left out
- A provider interface. A data source is a plain class with whatever lookups suit it. Standardize once a second real source needs a shared shape.
- Registry indexing. Virtual pages aren't in the registry, so
find(), search and sitemaps don't see them. Add asourcecolumn and a boot-time load if that is ever needed. - A
save()guard. Nothing stopssave()on a virtual page from writing real files. Virtual pages only come out of GET handlers and never reach the admin edit flow, so add a guard only if that stops being true.