Blogroll
In meinem Blog gibt es seit geraumer Zeit eine Blogroll. Was genau es damit auf sich hat, habe ich hier beschrieben. An dieser Stelle möchte ich erklären, wie ich meine Blogroll mit Kirby umgesetzt habe.
Meine Blogroll ist ziemlich simpel aufgebaut: In meinem Blog-Blueprint habe ich ein Structure-Feld, welches lediglich aus zwei Feldern besteht:
- Der URL des Blogs
- Dem Titel des Blogs
Bisher wurde unter meinem Bloglisting unter anderem die Blogroll in Icon-Form angezeigt. Das möchte ich erweitern, sodass die Blogroll auch unter /blog/blogroll erreichbar ist und ich somit einen Deeplink habe.
Da ich die Blogroll nicht als eigenständige Seite im CMS liegen haben möchte, werde ich eine Route für die Blogroll anlegen und dort eine virtuelle Seite ausspielen.
Die Route
In der Datei site/config/config.php
lege ich eine neue Route. Ich möchte, dass die Blogroll unter allen Sprachen unter blog/blogroll
erreichbar ist:
[
'pattern' => 'blog/blogroll',
'language' => '*',
'action' => function ($language) {
// […]
}
]
Damit kann die Seite unter der entsprechenden URL ausgeliefert werden. Würden wir diese URL jetzt aufrufen, bekämen wir allerdings einen 404-Fehler an den Kopf geworfen. Eine Route benötigt immer entweder eine Ausgabe oder muss auf eine andere Route weiterleiten.
Deshalb lege ich im nächsten Schritt eine virtuelle Seite an, die wir dann zurückgeben können:
$data = [
'slug' => 'blogroll',
'parent' => page('blog'),
'template' => 'listing-blogroll',
'translations' => []
];
$page = Page::factory($data);
site()->visit($page, $language);
return $page;
Zunächst lege ich den Slug fest, also blogroll
, dann gebe ich die übergeordnete Seite an, in meinem Fall das Blog. Schließlich benötigt die Seite noch ein Template, damit sie gerendert werden kann. Ich verwende hier ein spezielles Blogroll-Template. Auf die Übersetzungen werde ich später noch zu sprechen kommen.
Template
Ich werde hier nicht das komplette Template beschreiben, sondern nur die relevanten Teile. Das sind im Wesentlichen der Seitenkopf mit einer kurzen Beschreibung und das eigentliche Listing.
Der Seitenkopf besteht aus dem Titel und einer kurzen Beschreibung:
<div class="page-intro">
<h1>= $page->title(); ?>h1>
= $page->intro()->kt(); ?>
div>
Das Listing verwendet ein Snippet, welches ich in den Notizen schon im Gebrauch habe:
<ul class="note-list">
foreach ($blogroll as $blog) : ?>
$site->organism('list-entry-note', ['note' => $blog]); ?>
endforeach; ?>
ul>
Hier sei kurz angemerkt, dass ich für Snippets zwei Site-Methods nutzen, die ich mir selbst geschrieben haben. In diesem Fall organism()
. Im Grunde funktionieren sie genauso wie Kirbys snippet()
Funktion. Ich nutze meine Methoden, um etwas mehr Ordnung in meine Snippets zu bekommen. Darauf komme ich bestimmt in einem anderen Beitrag noch zu sprechen.
Mein Snippet list-entry-note
bekommt also ein Blog aus meiner Blogroll hereingereicht und stellt es dann dar. Es braucht drei Informationen:
- Einen Titel
- Eine Url
- Ein Icon
Die Daten kommen aus dem entsprechenden Controller
Der Controller
Controller dienen in Kirby dazu, die Templates von Datenlogik zu befreien. Ich mag den Ansatz und versuche deshalb meine Templates möglichst dumm zu halten. Alles, was mit Daten zu tun hat, sollte möglichst im Controller passieren.
So sieht der Controller für die Blogroll aus:
return function ($page) {
$blogroll = page('blog')->blogroll()->toStructure();
$blogEntries = [];
foreach ($blogroll as $blog) {
$url = str_replace(['https://', 'http://', '/'], ['', '', ''], Url::stripPath($blog->url()->value()));
$icon = $page->getSiteIcon($url);
$blogEntries[] = new StructureObject([
'content' => [
'title' => $blog->title(),
'url' => $blog->url(),
'icon' => $icon,
'intendedTemplate' => 'blogroll'
]
]);
}
$blogrollStructure = new Structure($blogEntries);
return [
'blogroll' => $blogrollStructure,
];
};
Zunächst hole ich mir das Blog, denn dort sind die Daten hinterlegt. Ich greife direkt auf das Blogroll-Feld zu und lasse es mir als Struktur zurückgeben. Dann fülle ich das $blogEntries
Array mit Daten. Es bekommt jeweils den Titel, die Url und ein Icon.
Für das Icon verwende ich eine eigene Methode, die versucht, das Favicon einer Seite zu holen und ggf. einen Fallback liefert. Das Ergebnis ist ein data:image/png;base64
String. Also keine Bild-URL. Das ermöglicht mir, die Favicons eine Weile zu cachen. So muss ich nicht bei jedem Seitenaufruf etliche andere Seiten anfragen (dazu in einem anderen Post mal mehr).
Schließlich erzeuge ich mir aus dem Array eine neue Struktur, denn mein Note-Entry-Snippet erwartet das so (es bekommt ja normalerweise eine Notiz rein und das ist eine Kirby-Page).
Abrunden der virtuellen Seite
Jetzt bin ich fast so weit, es fehlen noch ein paar Informationen in der virtuellen Seite, denn diese benötigt u. a. einen Titel und eine Beschreibung. Diese verstecken sich in den Übersetzungen. Ich habe mich dazu entschiede diese Daten zunächst nicht im Panel zu pflegen, weil ich sie vermutlich nicht besonders häufig anpassen werde:
'translations' => [
'en' => [
'code' => 'en',
'content' => [
'title' => 'Blogroll',
'date' => '2024-01-30',
'intro' => 'Blog I read regularly and can recommend.',
]
],
'de' => [
'code' => 'de',
'content' => [
'title' => 'Blogroll',
'date' => '2024-01-30',
'intro' => 'Blogs, die ich regelmäßig lese und die ich empfehlen kann.',
'uuid' => Uuid::generate(),
]
]
],
Wie man sieht, stehen die nötigen Daten nun in der virtuellen Seite. Die deutsche Sprachvariante bekommt zusätzlich noch eine uuid, sie ist bei mir die Hauptsprache.
Die fertige Blogroll
Damit ist die Seite nun unter blog/blogroll
abrufbar. Das Template bekommt die Daten vom Controller und rendert sie mithilfe des Snippets. Das Resultat könnt ihr hier sehen.
Neue Einträge kann ich somit weiterhin einfach im Blueprint vom Blog vornehmen und die virtuelle Seite kümmert sich um den Rest, ohne, dass ich im Panel noch extra eine Seite dafür anlegen muss.