Handbuch · 10

Dynamische Seiten (Catch-All)

Du willst unter /blog eine Liste und unter /blog/mein-artikel den Artikel – ohne für jeden Artikel eine eigene Seite anzulegen? Dafür gibt es Catch-All.

  1. Seite /blog anlegen und unter Einstellungen Catch-All anhaken.
  2. In der Collection blog ein Feld slug anlegen (der URL-Teil jedes Artikels).
  3. HTML der Seite /blog:
{% if route.slug %}
  {% with post=item("blog", "slug", route.slug) %}
    {% if not post %}{{ not_found() }}{% endif %}
    <article>
      <h1>{{ post.titel }}</h1>
      <p class="datum">{{ post.datum }}</p>
      {{ image(post.bild, "(min-width: 900px) 60vw, 100vw") }}
      {{ post.text|markdown }}
      <a href="/blog">← Alle Artikel</a>
    </article>
  {% endwith %}
{% else %}
  <h1>Blog</h1>
  {% for post in data("blog", "sort", "-datum") %}
    <a class="teaser" href="/blog/{{ post.slug }}">
      <h2>{{ post.titel }}</h2>
      <p>{{ post.text|truncatechars:160 }}</p>
    </a>
  {% endfor %}
{% endif %}
  1. SEO-Titel der Seite:
{% if route.slug %}{{ item("blog", "slug", route.slug).titel }} – {% endif %}Blog

So funktioniert es:

  • /blog → route.slug ist leer → Liste.
  • /blog/mein-artikel → es gibt keine Seite mein-artikel → die Catch-All-Seite antwortet mit route.slug = "mein-artikel".
  • /blog/gibts-nicht → item() findet nichts → not_found() liefert die 404-Seite mit Status 404.
  • Echte Unterseiten haben Vorrang: Legst du /blog/archiv als normale Seite an, wird diese angezeigt.
  • Mehrstufige Pfade: /blog/2025/rueckblick ergibt route.slug = "2025/rueckblick", route.rest.0 = "2025", route.rest.1 = "rueckblick".

Zum Testen hat die Vorschau bei Catch-All-Seiten das Feld Unterpfad. Ruft das Template not_found() auf (z. B. ohne Unterpfad), zeigt die Vorschau statt eines Fehlers einen Hinweis mit vorhandenen Unterpfaden – aus der Sitemap-Quelle bzw. der erkannten Collection.

Sitemap: Steht im HTML, im SEO-Titel oder im Head-Feld der Catch-All-Seite item("<collection>", "<feld>", route.slug), nimmt IllusionCMS alle veröffentlichten Einträge dieser Collection als Detailseiten in die /sitemap.xml auf (Entwürfe und Einträge ohne Wert im Feld nicht). Echte Unterseiten gehen vor.

Detailseiten aus anderen Quellen: Liest die Seite ihre Daten anders – aus IllusionDB, einer REST-Quelle oder per Workflow –, legst du unter Einstellungen der Catch-All-Seite bei Detailseiten für Sitemap und statischen Export die Quelle fest:

  • IllusionDB-Tabelle: Verbindung, Tabelle, die Spalte mit dem URL-Teil (z. B. slug), optional eine Spalte mit dem Änderungsdatum (<lastmod>) und Regeln oder eine gespeicherte Ansicht als Filter – z. B. nur aktiv = true.
  • Workflow: der HTTP-Eingang eines Workflows (wie bei workflow()), der eine Liste liefert – Objekte mit dem URL-Feld oder einfach Texte. Er bekommt sitemap=1 in msg.payload mit. So kommen auch REST-Quellen und berechnete Listen in die Sitemap.
  • Prüfen zeigt, wie viele Detailseiten herauskommen, und die ersten Adressen. Werte mit / ergeben mehrstufige Pfade (berlin/zahnarzt → /leistungen/berlin/zahnarzt).
  • Aus IllusionDB werden nur die Spalte mit dem URL-Teil und das Datum abgefragt.
  • Die Liste wird 10 Minuten zwischengespeichert. Ist die Quelle mal nicht erreichbar, bleibt der letzte Stand in der Sitemap, statt dass Tausende Adressen verschwinden.
  • Ab 50 000 Adressen wird /sitemap.xml automatisch ein Sitemap-Index mit /sitemap-1.xml, /sitemap-2.xml, … (Grenze der Suchmaschinen je Datei).
  • Dieselben Detailseiten nimmt der statische Export mit – auch wenn nichts auf sie verlinkt.

Daten aus IllusionDB: Detailseiten können ihre Datensätze auch direkt aus einer Datenbank holen – über einen Workflow mit dem Node IllusionDB, siehe Workflows → IllusionDB.