Creating a new route to remove the word 'tag' or 'category' from url

Hi all

I’ve created a blog, and used the same method as with tags, to filter by category. I have it working okay but now I would like to create a route to remove the /category; from the url. Sonja has helped me with a route the other day, and it works but for a totally different way of filtering. Now I’ve filtered in a new way and would like to create a new route.

I’ve looked at the documentation and really can’t get it working…

my url looks like this site.com/magazin/category:Sport I would like it to say site.com/magazin/sport

I’ve tried this code but that only removes the ‘magazin’ part out…


return [
    'routes' => [
        [
            'pattern' => '(:any)',
            'action'  => function($uid) {
                $page = page($uid);
                if(!$page) $page = page('blog/' . $uid);
                if(!$page) $page = site()->errorPage();
                return site()->visit($page);
            }
        ],
        [
            'pattern' => 'blog/(:any)',
            'action'  => function($uid) {
                go($uid);
            }
        ]
    ]
];

Any help please?

Why does your route use blog instead of magazin? You should have a route called

'magazin/(:any)'

And apart from that, it should be like described here:

1 Like

So this is my new route

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

When I manually type in magazin/sport - magazin opens up but it’s not filtered by sport category. However if I use the link and open up magazin/category;Sport then it filters…

I assume you didn’t follow the recipe properly, because you cannot filter by param anymore if you don’t use params.

So I should try to use the filtering method presented in the recipe which is this?

<?php

return function ($page, $tag) {

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

    if ($tag) {
        $articles = $articles->filterBy('tags', $tag, ',');
    }

    return [
        'articles' => $articles
    ];

};

Yes, of course. You cannot just use half of the recipe, it works all together. But you have to change the field name, because you want to filter by category, right?

1 Like

Okay I got it working… Only other thing I haven’t done is the lower case category in the url. Any suggestions with that?

This would work if the field contains a single value (i.e. select field, not multiselect or tags):

$articles = $articles->filter(fn ($article) => $article->category()->lower() === $category);

With this - what should my link look like?

This is what it currently looks like.

<a href="<?= page('magazin')->url() .'/'. esc($category, 'url') ?>"> <?= $category ?></a>

Str::lower($category)

Okay so that works, however the filter doesn’t seem to work. I’m not getting any results now…

What is actually stored in your category field? Only one value or a comma separated list of values?

It’s always only one value.

Ok, can you give me an example of the value stored in the content file, an example url that doesn’t work and the final filtering code?

example value

----
Category: Sport
----

But I also have examples where Category is such as ‘Priča Se’.

Url

http://localhost:8888/magazin/sport

Filter

   if ($category) {
          $articles = $articles->filter(fn ($article) => $article->category()->lower() === $category);;
        }

I know what the issue is.

$article->category()->lower()

doesn’t return a string but a field object, so we need to get the value

$articles = $articles->filter(fn ($article) => $article->category()->lower()->value() === $category);

That just goes to the 404 error page.

I think I would need to see your project, we are going in circles, it seems.

Okay

How do I show you my project?

The reason why you are getting the error is that you added a second routes key in your config instead of adding the new route to the first array, thus overwriting the original route.

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

            },
        ],
        [
            'pattern' => 'logout',
            'action'  => function () {

                if ($user = kirby()->user()) {
                    $user->logout();
                }

                go('login');

            },
        ],
    ],

Secondly, you need to urlencode/decode your tags.

In the template:

<a href="<?= page('magazin')->url() .'/'. urlencode(Str::lower($category)) ?>" class="button btn-small btn-red rounded text-normal ms-1"> <?= $category ?> <span class="ms-2 badge text-bg-light"><?= $page->children()->filterBy('category', $category)->count() ?></span></a>

In the controller:

if ($category) {
    $articles = $articles->filter(fn ($article) => $article->category()->lower()->value() === urldecode($category));
}