Content representation for virtual page

I’m using virtual pages in a project and I wanted to set up a JSON content representation. It seems like this is not possible?

Normally, just adding a template.json.php to the templates folder is enough to make the content representation work. Do I have to do something special for virtual pages to work?

How have you implemented that virtual page? Is it reachable via the site index?

Thanks for your reply, @distantnative! It’s a virtual page with virtual children, and I after looking into it some more, I think that’s the issue. I could not reproduce the issue when using a simple, single layer virtual page.

Here’s a simplified example…

The publications page has a virtual “topics” page, defined in its model:

<?php

use Kirby\Cms\Pages;

/**
 * PublicationsPage class
 * 
 * This class extends the Page class to create a virtual page for the topic tags container
 * and manages the combination of real and virtual child pages.
 */
class PublicationsPage extends Page
{
    /**
     * Get the real children of the page
     *
     * @return Pages Collection of real child pages
     */
    public function realChildren(): Pages
    {
        return Pages::factory($this->inventory()['children'], $this);
    }

    /**
     * Get or create the topic tags container
     *
     * @return Pages Collection containing the topic tags container
     */
    public function getTopicTagsContainer(): Pages
    {
        return Pages::factory([
            [
                'slug' => 'topics',
                'template' => 'topics',
                'model' => 'topics',
                'content' => [
                    'title' => 'Topics',
                    'uuid' => $this->uuid() . '-topics',
                ],
            ]
        ], $this);
    }

    /**
     * Get all children, merging real children with the virtual topic tags container
     *
     * @return Pages Combined collection of real and virtual children
     */
    public function children(): Pages
    {
        return $this->realChildren()->merge($this->getTopicTagsContainer());
    }

}

The (virtual) topics page has virtual children itself, similarly defined in the model:

<?php

use Kirby\Cms\Pages;
use Kirby\Toolkit\Str;

/**
 * TopicsPage class
 * 
 * Generates virtual child pages for topic tags defined in the parent page's structure field.
 */
class TopicsPage extends Page
{
    /**
     * Generate and return virtual child pages for topic tags
     *
     * @return Pages Collection of virtual topic tag pages
     */
    public function children(): Pages
    {
        $topicTags = $this->parent()->topic_list()->toStructure();

        $topicTagsPages = $this->generateTopicTagsPages($topicTags);

        return Pages::factory($topicTagsPages, $this);
    }

    /**
     * Generate an array of topic tag pages
     *
     * @param \Kirby\Cms\Structure $topicTags Sorted structure of topic tags
     * @return array Array of topic tag page data
     */
    private function generateTopicTagsPages(\Kirby\Cms\Structure $topicTags): array
    {
        $topicTagsPages = [];
        $usedSlugs = [];

        foreach ($topicTags as $num => $topicTag) {
            $usedSlugs[] = $slug;

            $topicTagsPages[] = [
                'slug' => $topicTag->topic_title()->slug(),
                'num' => $num + 1,
                'template' => 'topic',
                'model' => 'topic',
                'content' => [
                    'title' => $topicTag->topic_title()->value(),
                    'uuid' => $slug
                ]
            ];
        }

        return $topicTagsPages;
    }

}

These topic pages are the ones I want to create content representations of. Unfortunately that doesn’t work and I get a 404 instead.

Can those be accessed via $site->find($path)?

Yes, I can access them via $site->find('publications/topics/topic').

I could get it running for me.

My guess is that you don’t have a regular topic.php template. Without it the content representation doesn’t work either.

Thank you for testing, Nico. I do have a topic.php template, so I guess I’ll have to find the other difference between your setup and mine.

Maybe try to peel back layer by layer. E.g. if the virtual topics page works with/without merging with real children.

Because I think other than that I added it 1:1 as you posted above.

Except that in generateTopicTagsPages $slug didn’t exist

I finally had time to dig into this some deeper. Thanks again for testing it yourself, Nico.

In the end, it was not what I assumed previously — nested virtual pages work just fine (it must be pure magic). The culprit was a route with an aggressive (:all) pattern. I didn’t expect it to also catch the .json content representation. Makes total sense, though.

1 Like