Different page templates for frontend

Hi all,

I’d like to load another page template (PHP-File) based on the current page URI e.g:
‘/news’ ← ‘articles.php’
‘/news/world’ ← ‘articles–world.php’

In the panel it should be the same blueprint. This would provide the freedom to completely restyle the frontend for selected pages without backend-overhead.

I have archieved this with snippets already (with core components snippet loader), but with page templates… no success so far.

Any hints?

You could overwrite the $page->template() method in a page model.

Thank you @texnixe,

I solved it quite simple now.

Came up with a custom page-method that returns existing alternative template files:

'pageMethods' => [
  /**
    * Extend default template-names with alternative templates specified for each page
    * 
    * To use alternative page template, add this code on top of the template:
    * <?php if ($s = $page->alternativeTemplateFile($file)) return include $s ?>
    * 
    * Then create an template-file in the same folder of the template.
    * The filename syntax for your alternative page snippets is:
    * yourtemplatename--[page-uri]--[page-uri].php
    * e.g: content--legal--privacy-policy.php
    * 
    * It also supports specified languages:
    * yourtemplatename--[page-uri]--[page-uri]--[language-code].php
    * e.g: content--legal--privacy-policy--de.php
    */
  'alternativeTemplateFile' => function($file) {
    $pathinfo = pathinfo($file);
    $name = $pathinfo['filename'];
    $lngDefault = kirby()->defaultLanguage()->code();
    $lngCurrent = kirby()->currentLanguage()->code();

    $names = [
      // add name for current page in current language
      $name .'--'. str_replace('/', '--', $this->uri($lngDefault)) .'--'. $lngCurrent,

      // add name for current page in default language
      $name .'--'. str_replace('/', '--', $this->uri($lngDefault)),
    ];
    
    foreach($names as $s) {
      $src = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . $s .'.php';

      if (is_file($src)) {
        return $src;
      }
    }
  }
]

… and put a one-liner on top of page templates that allow alternative page templates. It finds alternative page templates based on uri and includes it.

<?php if ($s = $page->alternativeTemplateFile($file)) return include $s ?>
<!DOCTYPE html>
...

This way, there’s even control for support for each template.

Oh, and the same works for snippets (not even the one-liner needed):

'components' => [
  /**
    * Extend default snippet-names with alternative snippets specified for each page
    * 
    * To use alternative page snippets, create extra snippet-files in the same folder,
    * the filename syntax for your alternative page snippets is:
    * yoursnippetname--[page-uri]--[page-uri].php
    * e.g: header--legal--privacy-policy.php
    * 
    * It also supports specified languages:
    * yoursnippetname--[page-uri]--[page-uri]--[language-code].php
    * e.g: header--legal--privacy-policy--de.php
    * 
    * @source: https://getkirby.com/docs/reference/plugins/components/snippet
    */
  'snippet' => function (Kirby $kirby, string|array $name, array $data = [], bool $slots = false): Snippet|string {
    $names = (gettype($name) == 'string') ? [$name] : $name; // convert to array

    $namesExtended = [];

    foreach ($names as $s) {
      // add name for current page in current language
      $namesExtended[] = $s .'--'. str_replace('/', '--', $kirby->site()->page()->uri($kirby->defaultLanguage()->code())) .'--'. $kirby->currentLanguage()->code();
      
      // add name for current page in default language        
      $namesExtended[] = $s .'--'. str_replace('/', '--', $kirby->site()->page()->uri($kirby->defaultLanguage()->code()));

      // add original name
      $namesExtended[] = $s;
    }

    return Snippet::factory($namesExtended, $data, $slots);
  }
]

So, with this its possible to have different templates/snippets for each page and language.

Kirby <3