Use Kirby content representations for custom view modes

17.02.2025

With content representations Kirby has a powerful way to display the content of a page in a different way. Usually, this refers to a single page when it is requested at a specific URL. I also work a lot with Drupal and I appreciate the concept of view modes where the content of a page type is prepared for different views, but is not linked to a specific URL like with content representations. The classic example is the teaser view, i.e. how a page is displayed as a teaser within another page.

My idea is to use the existing system of content representations for view modes. In the end, I would like to be able to write something like this in my template:

<?php foreach ($kirby->collection('different-teaser-pages') as $teaser): ?>
  <?= $teaser->viewMode('teaser') ?>
<?php endforeach ?>

Afterwards the different page types are rendered in the “teaser” view mode, whereby each type defines how this should look like by itself. This can be very similar to each other, but also completely different.

By the way, how to collect pages using the collection() method like in the example can be found in the Kirby Collections Guide.

Content representations

Let's start simple. Content representations are usually used to display the content of a page in a different format, for example as JSON, XML, PDF, ... you name it. If you are not yet familiar with this, you should take a closer look at the following link. It also describes how the additional templates must be named and via which URL they can then be accessed. This is important for the further procedure.

https://getkirby.com/docs/guide/templates/content-representations

To create a template for a teaser variant of a page, we create a separate file in the template directory for each page type. Let's use “news” and “event” as example.

site/templates/news.teaser.php
site/templates/event.teaser.php

We can now define the markup for the “teaser” view mode in this template with the same possibilities as in any other page template. You can use snippets or simplify the code with the help of representation controllers which are based on general controllers.

An example template for the news teaser would be …

# site/templates/news.teaser.php

<?php 
  snippet('card', [
    'title' => $page->title(),
    'text' => $page->content()->introtext()->kti(),
    'url' => $page->url(),
  ]);
?>

For an event we use a different template.

# site/templates/event.teaser.php

<section class="event-teaser">
  <h2><?= t('Event') ?></h2>
  <p><?= $page->content()->text()->kti() ?></p>
  <ul>
    <?php foreach ($page->categories()->split('|') as $category): ?>
    <li><?= $category ?></li>
    <?php endforeach ?>
  </ul>
</section>

The markup is of course only an example. However, it is important that the template is not a complete HTML document with <head> and <body>, but only contains the markup of the component. After all, it is to be output later within an existing page.

Custom page methods

Now we need to be able to use the templates we have just created. With custom page methods, Kirby offers an extremely flexible tool for extending the page object with your own methods. We want to use this to call our view modes. For this, we need to create our own plugin with at least an index.php file.

Kirby::plugin('flokosiol/viewmode', [
  'pageMethods' => [
    'viewMode' => function ($viewMode = '') {
      $html = '';
      if (!empty($viewMode)) {
        try {
          // render the page as a $viewMode content representation
          $html = $this->render([], $viewMode);
        } finally {
          return $html;
        }
      }
      return $html;
    },
  ],
]);

Now we are able to use the new viewMode() method for our $page object as mentioned at the very beginning. If we call a view mode which doesn't exists, the page method will return an empty string to prevent any errors.

<?= $page->viewMode('teaser') ?>

Route blocking

Content representations are by design accessible via their URL. This is intended! Otherwise they wouldn't make any sense. But since we use it for a different purpose, we might want to block these pages. For example, if the regular page is https://mydomain.com/news/my-latest-article a view mode teaser would be accessible via https://mydomain.com/news/my-latest-article.teaser.

To block all URLs for the teaser view mode we need to set up a custom route which blocks all URLs ending on .teaser. Obviously you should double check, that no regular page ends with this pattern!

We need to extend our plugin a little bit to achieve this. Multiple route patterns can be combined as an array. The following example shows another route blocking pattern for an “event“ view mode.

Kirby::plugin('flokosiol/viewmode', [
  'pageMethods' => [
    // see above
  ],
  'routes' => function ($kirby) {
    return [
      [
        'pattern' => ['(:all).teaser', '(:all).event'],
        'action' => function () {
          return false;
        }
      ],
    ];
  },
]);

Ready to use plugin

To make this feature ready to use for everyone, I wrapped everything in a plugin.

https://github.com/flokosiol/kirby-viewmode

Links


CMS Kirby PHP OpenSource #Plugin

What do you think?

Let's discuss on Mastodon