Generate Category Pages

Hi people,
I do not know how to create pages for categories like in the build-in feature tags. I know that I should use routing for this. It is hard for me to create from scratch like this. It would be awesome if you would have a simple gist for me which I can use…

What I want:
Search for all available categories, create pages and make it public via Url.

Thank you very much!

I assume what you are trying to do here is to filter pages by category? So each category page shows the filtered children of a “blog” type parent? But with the difference that you don’t want to use parameters like blog/tags:blue but blog/blue?

I have tags and categories for a post. When I click on a tag, I will get a page with all tag related articles. I want the same for my field categories. If you click on Guides, I want to get a page with all guides related articles :slight_smile:

Ah, ok, you can do it in the same way as with your tags, only use category as parameter.

I don’t think so. I got an error, when I do it like tags. When I click on “Guides” I come to the homepage.

http://localhost:8888/blog/category:Guide

Could you please post your code? I’d say your links are not correct.

post.text.php:

<?php snippet('header') ?>

<main class="o-article" role="main">
  <article>
    <div class="o-head-up">
      <?= $page->image() ?>
      <a href="" class="c-button c-button--ghost c-button--rounded o-head-up__top-left" title="Zurück">
        <svg class="c-icon c-icon--s" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
        	 width="92px" height="92px" viewBox="0 0 92 92" enable-background="new 0 0 92 92" xml:space="preserve">
        <path id="XMLID_546_" d="M84,46c0,2.2-1.8,4-4,4H21.6l18.1,18.2c1.6,1.6,1.6,4.1,0,5.7C39,74.6,38,75,36.9,75c-1,0-2.1-0.4-2.8-1.2
        	l-24.9-25c-1.6-1.6-1.6-4.1,0-5.6l24.9-25c1.6-1.6,4.1-1.6,5.7,0c1.6,1.6,1.6,4.1,0,5.7L21.6,42H80C82.2,42,84,43.8,84,46z"/>
        </svg>
      </a>
      <?php foreach($page->categories()->split() as $category): ?>
        <li class="o-list-inline__item">
          <a href="<?= url('blog', ['params' => ['category' => $category]]) ?>" title="" class="c-chip">
            <?= html($category) ?>
          </a>
        </li>
      <?php endforeach ?>
    </div>
    <header class="o-container-medium o-distance-edge o-distance-l">
      <a class="c-badge" href="<?= $site->url() . '/category/' . urlencode($page->category()) ?>"><?= $page->category() ?></a>
      <h1 class="c-title o-distance-s"><?= $page->title() ?></h1>
      <div class="o-distance-m">
        <time class="t-text-quiet" datetime="<?= $page->date()->toDate('c') ?>"><?= $page->date()->toDate('%d. %B %Y') ?></time>
      </div>
    </header>

    <div class="o-article__body o-container-medium o-distance-component o-distance-edge">
      <?= $page->text()->kt() ?>
    </div>
  </article>
  <div class="o-container-medium o-distance-edge o-distance-xl">
    <ul class="o-list-inline">
      <?php foreach($page->tags()->split() as $tag): ?>
        <li class="o-list-inline__item">
          <a href="<?= url('blog', ['params' => ['tag' => $tag]]) ?>" title="" class="c-chip">
            <?= html($tag) ?>
          </a>
        </li>
      <?php endforeach ?>
    </ul>
  </div>
  <?= snippet('widget-newsletter') ?>
  <section>
    <?php
      $related = $page->related()->toPages();
      if ($related->count() > 0):
      ?>
        <div class="o-container o-distance-container o-distance-edge">
          <h2 class="c-title u-text-center">Könnte dich noch interessieren</h2>
          <div class="o-stream o-distance-component">
            <?php foreach($related as $article): ?>
              <?php snippet('article', ['article' => $article, 'className' => 'o-stream__item']) ?>
            <?php endforeach ?>
          </div>
        </div>
      <?php endif ?>
  </section>
</main>

<?php snippet('footer') ?>

post.text.txt

Title: So kommst du als Zugezogener im Dorf an

----

Text:

Du ziehst nicht einfach nur in einen Ort. Du begibst dich in den Kreis, wo sich Leute seit Generationen kennen. Es ist nicht leicht in die Gemeinschaft anzukommen, doch mit unseren Tipps ist das kein Problem mehr. Du musst nur verstehen, wie der Hahn dort kräht.

Wahrscheinlich bist du etwas aufgeregt als würdest du gleich zu einem Vorstellungsgespräch fahren. Zurecht. Du wirst definitiv erstmal auf die Probe gestellt. Deine erste Dorfveranstaltung, auf der du bist, wird dein Bewerbungsgepräch werden. Der erste Eindruck zählt. Die Leute sind skeptisch gegenüber Neuen und sie müssen erstmal wissen, dass du ganz lieb bist und niemanden die Eier aus dem Stall klauen wirst.

## Lasse dich auf der Dorfveranstaltung blicken
Du wirst hier im Ort nicht viele Feste feiern. Du kannst sie an einer Hand abzählen. Die wenigen Feiern musst du nutzen, denn es gibt selten die Chance, dass du nach Hause stolpern kannst. Ich sage dir, dass wir Dorfkinder richtig feiern können. Es gibt wenige Parties bei denen du mal eben zu Fuß nach Hause stolpern kannst, doch die werden dann auch so richtig gefeiert. Für dich ist die erste Veranstaltung wie ein Bewerbungsgespräch. Gehst du nicht hin, machst du einen schlechten Eindruck. Zumindest musst du dich darauf nicht vorbereiten. Nur gibt es auch hier einige Tipps zu beachten.

## Das Schützenfest ist das Wacken der Dorfkinder.
Auf dem Fest gibt es in der einen Ecke die ältere Generation, die mal wieder für einen Schnack und Bratwurst-Pommes vorbeikommen. Sie leben seit der Geburt hier, haben das Dorf mit aufgebaut und bleiben meist nicht lange. Auf der anderen Seite findest du den harten Kern. Der ist bei jedem Fest gleich und bleibt bis zum Schluss. Bei dem ein oder anderen muss man am Ende die Schubkarre holen, damit derjenige auch mal nach Hause zu seiner Frau kommt. Die dritte Gruppe besteht aus Leuten, die meist nur etwas essen, ein Bier trinken und wieder nach Hause gehen. Für sie war es nur ein Pflichtbesuch, damit niemand sich darüber unterhält warum Familie Sievers nicht da war.

## Bei dem Lied "Aloha Heja He" musst du rudern.

## Gehe in den örtlichen Verein
Fast jeder aus dem Ort tut etwas für das Dorf, wo er schon Jahrzehnte lebt und geboren wurde. In meinem Dorf sind rund 150 Leute in der Feuerwehr und das obwohl wir nur 300 Einwohner sind. Davon sind einige aktiv dabei und andere helfen durch ihren Jahresbeitrag den Verein zu unterstützen. Die Feuerwehr kümmert sich die Planung und Durchführung des Osterfeuers, Laternenumzug und hilft dem Gesangverein bei ihren Festlichkeiten. Ohne sie würde einiges im Ort überhaupt nicht möglich sein. Deswegen weiß jeder wie wichtig das Ehrenamt oder der Verein ist und versucht diesen zu unterstützen.

Bist du nicht in dem Verein, wirst du immer wieder gefragt werden warum du nicht dabei bist und wann du das Eintrittsformular unterzeichnest - spätestens wenn der zehnte Fanta-Korn geflossen ist und du nachts um vier auf einem Bierdeckel deine Unterschrift setzt, bist du dabei. Doch wenn du dich von Anfang an integrieren möchtest, trete dem Verein bei und sei aktiv für den Ort. Du lernst vor allem sehr viele Leute kennen, hast einen Fuß in der Tür und tust noch etwas für den Ort.

## Lade zu einem Umtrunk ein
Du bist frisch zugezogen und musste dich nicht nur in der gesamten Dorfgemeinschaft beweisen. Deine Nachbarn werden zuerst skeptisch sein. Nun gilt es erstmal die Nachbarschaft zu überzeugen und alles andere ergibt sich dann. Also zögere nicht, schmeiß den Grill an und pack die Kiste Bier auf den Tisch. Wenn jemand zu viel getrunken hat, machst du kurz den Taxiservice mit der Schubkarre und bringst ihn nach Hause. Am nächsten Tag weiß jeder im Dorf, dass du feiern kannst und wirst zu anderen Parties eingeladen.

## Unterhalte dich mit den Leuten
In der Stadt laufen die Leute gestresst zur nächsten U-Bahn. Auf dem Dorf geht derjenige lieber die drei Kilometer, bevor er zwei Stunden auf den Bus warten muss. Hier drehen sich die Uhren etwas langsamer. Die Leute haben Zeit. Nein. Sie nehmen sich diese Zeit. Solltest du mal eben ins Dorf gehen wollen, triffst du eigentlich immer jemanden. Wahrscheinlich ist Helmut wieder am Dienstagnachmittag draußen im Vorgarten und mäht den Rasen. Wenn es sich ergibt quatsche mit den Leuten und laufe nicht einfach blind daran vorbei. Ansonsten bist du bald derjenige im Ort der keinen grüßt. Außerdem bekommst du am Rande immer wieder Insider über deine Nachbarn.

## Die Geste zählt. Nicht das Geld.
Etwas dass heutzutage immer weniger wird, doch bei uns hat die Geste noch einen hohen Stellenwert. Bei uns wird sich gegenseitig geholfen, wenn der Andere in Not ist oder jemanden braucht. Wenn Helmut dir deinen Baum im Garten fällt, dann wird er kein Geld von dir verlangen. Stattdessen freut er sich, wenn ihr gemeinsam danach noch ein Bierchen zusammen zischt. Währenddessen wird der Baum klein gemacht um beim nächsten Winter für die warme Bude zu sorgen.

Miteinander. Ein Wort das hier wirklich großgeschrieben wird. Hier lebt niemand für sich. Wir sind eine große Gemeinschaft und für den anderen da. Auch wenn es von dir nicht verlangt wird, solltest du dich erkenntlich zeigen. Du musst nicht mal Geld ausgeben. Sei einfach hilfsbereit und beim nächsten Mal für den Nachbar da.

## Kurz zusammengefasst
Ich fasse dir einmal die wichtigsten Punkte zusammen, die du als Zugezogener machen kannst, damit du schnell im Dorf aufgenommen wirst.

- Gehe zu Dorfveranstaltungen (gib eine Runde aus)
- Trete einem Verein bei
- Lade zu einem Umtrunk ein
- Unterhalte dich mit den Leuten
- Zeige dich erkenntlich - die Geste zählt

Hoffentlich konnte ich dir mit meinen Tipps für einen guten Start ins Dorfleben helfen. Um noch mehr übers Dorfleben zu erfahren, folge mir gerne bei (link: https://instagram.com/dorfstattstadt text: Instagram.)

----

Gallery:

- auswaerts21x9.jpg

----

Date: 2019-10-13 18:05

----

Author:

- hallo@dorfstattstadt.de

----

Categories: Guides

----

Tags: Zugezogener, Hilfe

----

Related:

- >
  blog/die-funf-typen-hast-du-auf-jedem-dorffest
- blog/auto-uberbrucken-leichtgemacht
- >
  blog/gefahrdet-der-frost-die-erdbeerernte

----

Category: Guides

This is how you create your tags URL:

And this is how you create your category URL:

<a class="c-badge" href="<?= $site->url() . '/category/' . urlencode($page->category()) ?>"><?= $page->category() ?></a>

If you create your category URL in the same way as your tags URL and filter in the same way in the controller, then it should work.

But you don’t want to use a parameter but a URL slug instead?

If you want a URL slug instead of the parameter, you need in fact a route:

'routes' => [
    [
        'pattern' => 'blog/category/(:any)',
        'action'  => function($category) {
             $data = [
                  'category' => $category,
                ];
            return page('blog')->render($data);
        }
    ]
]

(https://getkirby.com/docs/guide/routing#pass-data-to-controller)

Then you can use this data in your controller to filter the posts: https://getkirby.com/docs/guide/templates/controllers#arguments-from-page-render-in-route

1 Like

Thanks for your help. But I need the whole gist that I can only copy & paste it. Sorry for that, but I don’t know how I can do it in my code.

  1. I want a blogoverview-page like localhost:8888/blog

  2. I want a url for each category where are all articles from that category in a list localhost:8888/blog/{categoryname}/

Ok, then you have to change the route a bit:

'routes' => [
    [
        'pattern' => 'blog/(:any)',
        'action'  => function($category) {
             // since we use the same pattern for categories as for pages
             // we first check if there is a page and return it if true
             if ($page = page('blog/' . $category)) {
                 return $page;
             } 
             // we get an array of all categories from the blog pages and check
             // if the given value is in that array
             if (in_array($category, page('blog')->children()->listed()->pluck('category', ',', true))) {
                 // if that is the case, we render the blog page and send the
                 // category value to the controller
                 $data = [
                    'category' => $category,
                 ];
                return page('blog')->render($data);
             }
             // if none of the above exits the route, we send it to the next
             $this->next();
        }
    ]
]

Then in your blog.php controller:

return function ($page, $category) {

  $articles = $page->children()->listed();

    // your tags logic here

    // filter by category
    if ($category) {
        $articles = $articles->filterBy('category', $category, ',');
    }

    return [
        'articles' => $articles
    ];

};
2 Likes

Thank you :slight_smile: I think this help but there is something missing. How I can use a category template for a different view like at the blogpage?

If you need a separate template for the categories, I’d use a virtual page in the route:

    'routes' => [
        [
          'pattern' => 'blog/(:any)',
          'action'  => function ($category) {
    
            if ($page = page('blog/' . $category)) {
                return $page;
            } 
            if (in_array($category, page('blog')->children()->listed()->pluck('category', ',', true))) {
                // if that is the case, we return a virtual page

                return Page::factory([
                    'slug' => $category,
                    'template' => 'category',
                    'model' => 'category',
                    'content' => [
                        'title' => 'Results for ' . ucfirst($category),
                    ]
                ]);
            }
            $this->next()
                
          }
        ]
    ]

And in your category.php template:

<?php snippet('header') ?>
<h1><?= $page->title() ?></h1>

<?php 
$children = page('blog')->children()->filterBy('category', $page->slug(), ',') ;
dump($children);
?>
<?php snippet('footer') ?>

The alternative would be to use the same route and the blog template as in the first example, but use a different snippet for the view depending on whether or not $category is set.

Hmm :frowning: This is what I did - without a controller or something like that. It does not work, when I put the routes option in the config.

config.php

<?php
return [
    'debug' => true,
    'locale' => 'de_DE.UTF-8',
    'date.handler' => 'strftime',
    'routes' => [
        [
          'pattern' => 'blog/category/(:any)',
          'action'  => function ($category) {
    
            if ($page = page('blog/category/' . $category)) {
                return $page;
            } 
            if (in_array($category, page('blog')->children()->listed()->pluck('category', ',', true))) {
                // if that is the case, we return a virtual page

                return Page::factory([
                    'slug' => $category,
                    'template' => 'category',
                    'model' => 'category',
                    'content' => [
                        'title' => 'Results for ' . ucfirst($category),
                    ]
                ]);
            }
            $this->next();
          }
        ]
     ]
];

category.php

<?php snippet('header') ?>

<h1><?= $page->title() ?></h1>

<?php

$children = page('blog')->children()->filterBy('category', $page->slug(), ',') ;

dump($children);

?>

<?php snippet('footer') ?>

What does your content structure look like?

Here is the structure:

post.text.txt:

Title: An Weihnachten sind alle wieder da

----

Text:

**Der Wecker klingelt. Es ist sieben Uhr und Heiligabend. Ich muss noch meine Tasche packen und dann auch schon los auf die Autobahn. Es warten 180 Kilometer bis zur Heimat auf mich. Heute ist Heiligabend und ich sehe die Familie wieder. Wir sind gegen zehn Uhr zum Frühstücken verabredet. An Weihnachten sind eben alle wieder da.**

Ich fahre auf der Autobahn mit 130 entlang und frage mich, was eigentlich das Elternhaus für mich bedeutet. Es ist der zentrale Treffpunkt der ganzen Familie. Ich habe dort meine Kindheit verbracht und damals mit meiner Oma zusammengewohnt. Ganze Generationen sind dort groß geworden und das Haus ist voll mit schönen Erinnerungen an damals. Bevor ich noch weitere schöne Gedanken fassen konnte, musste ich auch schon auf die Landstraße abbiegen. Nun noch ein kurzes Stück und dann bin ich wieder Zuhause.

Auf der Straße zum Dorf angekommen wird meine Freude immer größer. Ich sehe das Ortsschild und werde langsamer. Ich muss nach links und rechts schauen. Eventuell sehe ich einen Nachbar. Doch heute war keiner draußen und ich stehe nun vor der Haustür. Daneben noch drei andere Autos, denn meine Geschwister samt Schwager sind auch wieder Zuhause. Kaum die Tür aufgeschlossen, kommt mir auch schon mein Neffe entgegen.

Alle freuen sich. Vor allem meine Eltern, die mich mit ihren freudestrahlenden Augen in den Arm nehmen. Ich glaube für die Eltern ist Weihnachten etwas ganz besonderes, denn alle Kinder sind wieder da. Früher haben wir uns jeden Tag gesehen. Heute freue ich mich, wenn ich sie einmal die Woche besuchen kann. Nun aber erstmal auf die Couch und über die Neuigkeiten quatschen, die letztens erst passiert sind. Ich habe einiges zu meinem Job zu berichten. Doch eh ich mich versehe ist es auch schon 15 Uhr und wir brechen auf in die Kirche.

## Warum gehe ich dieses Jahr besonders gerne in die Kirche?
Jedes Jahr entschied ich spontan, ob ich mit in die Kirche fahre. Diesmal ist es anders. Ich fahre mit und treffe auf Leute, die ich schon länger nicht mehr gesehen habe. Leute, mit denen ich damals im Verein Fußball gespielt habe oder noch aus der Schulzeit kenne. Das war auch meine große Hoffnung, weswegen ich unbedingt in die Kirche wollte. So schön wie das Wiedersehen auch ist, bin ich froh, dass der Gottestdienst schon vorbei ist. Es geht wieder nach Hause, wo meine Mutter das Essen vorbereitet hat. Es gibt Kartoffelsalat und Würstchen.

## Tradition bleibt Tradition
Nach dem Essen gehen die Kinder ins Obergeschoss, wo mittlerweile vier Räume leer stehen. Die anderen beiden Räume sind voll mit Sachen von mir und meinen Schwestern. Der andere Raum ist das Bad. Hier sitzen wir nun auf der Couch in einem Raum. Ich kann nicht mehr "mein Zimmer" sagen, doch es ist auch in Ordnung so. Ich höre, wie jemand die Treppe hochgeht. Mein Vater klopft an der Tür und sagt, dass wir nun runterkommen dürfen. Mit einem Zettel in der Hand folgen wir ihm.

Wir setzen uns alle auf die Couch und ich fange mit der Weihnachtsgeschichte an, die wir jedes Jahr neu aussuchen. Früher haben wir auf unserer Blockflöte etwas vorgespielt, doch das haben wir vor einigen Jahren durch eine Geschichte ersetzt. Die Geschenke ohne etwas zu präsentieren würde sich komisch anfühlen. Meine Schwester laß die letzte Passage vor. Nun wird es Zeit die Geschenke auszupacken. Also gehen wir Kinder zum Weihnachtsbaum und verteilen anhand der Namen die Geschenke an die Personen. Jeder muss erst seine Geschenke vor sich liegen haben bevor er auspacken darf. Nach all den Jahren geht das aber sehr schnell und wir fangen sie aufzureißen. Nebenbei nasche ich noch etwas von dem selbstgebauten Hexenhaus meines Opas, dass jedes Jahr aufgebaut werden muss. Tradition ist Tradition.

*So hätte ich mir Weihnachten wohl gewünscht. Wie früher als wir noch oben gesessen haben und bei meinen Eltern. Doch dort sind wir an Heiligabend schon seit letztem Jahr nicht mehr sondern wir sind bei meiner Schwester, was auf einer anderen Weise schön ist.*

## Der nächste Tag: Abreise
Alle sind völlig satt vom Mittagessen und liegen auf dem Sofa. Am ersten Weihnachtsfeiertag sind wir bei meinen Eltern. Wir schauen eine schöne Bescherung und ich schaue auf die Uhr. Nicht mehr lange und ich sitze im Auto auf dem Weg zurück nach Hause. Ich mag kein Abschied, aber wer hasst das nicht.

Ich schaue in die Runde und beobachte meinen Neffen. Er schläft so friedlich. Der Abschied von ihm und meinem Vater fällt mir jedes Mal am Schwersten. Doch so langsam muss ich meine Jacke holen. Zuerst sage ich meinen beiden Schwestern Tschüss. Danach umarme ich meinen Schwager. Meine Augen werden glasig. Nun mein Neffe. Unglaublich aber dieser kleine Kerl... Er macht nicht viel, ist einem aber sehr ans Herz gewachsen. Bevor mir jemand ein Taschentuch reicht, gehe ich aus dem Wohnzimmer und drücke meine Mum ganz fest. Sie war immer für uns Kinder da und nun sind alle aus dem Haus raus. Daran muss ich oft denken. Mein Vater begleitet mich zur Tür und mit jedem Schritt schlägt das Herz schneller, weil ich weiß was jetzt bevorsteht.

Wir umarmen uns und jetzt kann ich einfach nicht mehr. Die Situation ist so unangenehm, dass ich flüchte und traurig zum Auto gehe gehe. Der Motor springt an, doch mein schluchzen übertrifft jedes Geräusch. Ich schaue zum Haus und zu meinem Vater, an dem ich nun vorbeifahre. Ich glaube ich werde noch etwas Zeit brauchen um mit dieser Situation besser umgehen zu können. Doch es fühlt sich alles trotzdem richtig an und genau das was ich will. Jede Entscheidung zieht auf der anderen Seite eben auch Konsequenzen mit sich.

----

Cover:

- weihnachten-im-dorf.jpg

----

Date: 2017-12-26 14:00

----

Author:

- hallo@dorfstattstadt.de

----

Category: Landleben

----

Tags: Weihnachten

----

Related: 

----

Gallery:

This line doesn’t make sense, your blog posts are in the blog folder, not int the blog/category folder.

Hm, error 500 points at a server error. Is there any more information in the server logs?

Edit: Ok, there’s a missing semicolon after $this->next(). Strange that your debugging doesn’t tell you.

Sonja, it works now. I removed the if statement and now I have category pages. Thank you very much for your help and the patience! I think categories are often in blogs. It would be great to add this solution to the cookbook.

Yes, ok, with your pattern, the first if-statement is not necessary, that was only needed for the case that the categories would conflict with blog page URLs.

We already have an example here: https://getkirby.com/docs/cookbook/content/filter-via-route

It doesn’t use a different template though, maybe I’ll add this example.

Updated:

2 Likes