How to add virtual pages to kirby's index

Apologies to revive this old post, but it came the closest to my problem and I might have found a solution or add a missing link.

My situation is that I created a plugin to handle multiple API calls to different endpoints

example.com/v1/customerX/locations
example.com/v1/customerX/locations/1
example.com/v1/customerX/locations/2
example.com/v1/customerY/locations
example.com/v1/customerY/locations/5
example.com/v1/customerX/news{/:id}
example.com/v1/customerX/events{/:id}

For Kirby I setup a content folder:

content/
  customerX/site.txt
  customerY/site.txt

And my goal is to reuse the API endpoints and integrate them in kirby and be able to query all those virtual kirby pages like native pages.

my plugin index.php looks like this: (Work in Progress!)

load([
	'EntriesPage' => 'src/EntriesPage.php',
	'EntryPage' => 'src/EntryPage.php',
], __DIR__);


Kirby::plugin('mo/core', [
	'routes' => function($kirby) {
		return [
			[
				'pattern' => [
					'(:any)/locations' // needs more patterns?!
				],
				'language' => '*',
				'action' => function ($language, $parent) {
					$page = [
						'slug' => 'locations',
						'template' => 'locations',
						'num' => 0,
						'model' => 'entries',
						'parent' => page($parent),
						'children' => [],
						'content' => [
							'title' => t('Standorte', 'Standorte'),
						]
					];

					$page = new EntriesPage($page);
					return $page;

				},
			],
		];
	},

	'pageModels' => [
		'entries' => 'EntriesPage',
		'entry' => 'EntryPage',
	],
]);

The EntriesPage Model creates the children()

class EntriesPage extends EntryPage
{
	public function children() : Pages
	{

		// Query the `record` collection, could also be `geojson`
		$results = $this->_query('records/'. $this->slug());

		foreach(A::get($results,'records') as $item) {
			$pages[] = [
				'slug' => $item['id'],
				'num' => 0,
				'parent' => $this,
				'model' => 'entry',
				'template' => 'location',
				'content' => array_merge($item,[
					'title' => t($item['name_en'], $item['name'])
				])
			];
		}
		return Pages::factory($pages, $this);
	}

}

And EntryPage Model has a method to query the API.

So this works fine if I visit, mykirby.com/customerX/locations but if I want to show a list of locations as teaser on the parent parent page mykirby.com/customerX and try to access the location children with
page('customerX/locations'), the children aren’t registered with kirby, as @bvdputte described it initially.

@texnixe is correct when she says, VP are getting registered in the guide “Virtual Pages from a Database”.

In the Database example it comes down to creating a file in the content folder.
So when I added a locations/entries.txt to each customer:

content/
  customerX/site.txt
    locations/entries.txt
  customerY/site.txt

then customerX/locations wasn’t a virtual file anymore, and it showed up in the index with all the children. But I don’t want to add a bunch of ghost folders/files to my content, just to trick Kirby.

It comes down to Pages::inventory() that collects all files (children, images, templates) and sets the $page->children() somewhere in \HasChildren->index() when you attempt to find a page(’…’)

So my solution is to overwrite inventory in the site model like this:

/site/models/site.php


class SitePage extends Page
{
	public function inventory(): array
	{

		if ($this->inventory !== null) {
			return $this->inventory;
		}

		$kirby = $this->kirby();

		$this->inventory = Dir::inventory(
			$this->root(),
			$kirby->contentExtension(),
			$kirby->contentIgnore(),
			$kirby->multilang()
		);
		// register my virtual Parent Page
		$this->inventory['children'][] = [
			'dirname' => "locations",
			'model' => 'entries',
			'num' => 0,
			'root' => "M:\WEB\project\content\customerX/locations", //?
			'slug' => "locations",
		];

		return $this->inventory;
	}

}

And now everything works as expected.

I still have to make it more universal for my case and experiment with the virtual inventory, but this did the trick to register a virtual page with child pages.

Hopefully all of this makes sense, and maybe it helps someone find a solution.
@bvdputte recommended his plugin to me GitHub - bvdputte/kirby-vpkit: Virtual pages helper for multilingual Kirby 3 that deals with virtual pages. It’s a different approach, and worth to explore.

Any insight into how or why inventory() plays such an important role is appreciated. I just came to this conclusion by a lot trial and error and stepping through the code with Xdebug.

1 Like