Help with a route

So I have a little static html and vanilla JS app that will eventually be turned into a iOs and Android apps. It fetches data from a headless instance of Kirby but needs to hit a route to get back recommended prodcuts based on tags generated on the static site.

It basically shows a range of books and based on the range of books chosen by the user, it then returns a list of recceomended titles.

Im not really sure how to format the list to send to the route. Right now i have as the result:

const params = new URLSearchParams({

    genres: comichoices.toString(), // this comes from data in local storage

  });

Which outputs this to the console:

genres=Aliens%2CDark+Humour%2CAdult+Manga%2CDark+Humour%2CHumour%2CHumour

The raw comicchoices var is stored like this:

Aliens,Scifi,Dark Humour,Humour

Basically I need to send that through to a Kirby route which then returns a list of matching titles, by filtering the entire catalagoue of titles by those tags, then returning the result back to the static app as JSON.

Whats the best way to do that please?

Not sure if that’s the best way to do it but some thoughts:

  • You could use a custom route, but you could also use a content representation and use a controller. Depends on your setup what feels more fitting to the rest of the site.
  • In a route you can use $this->requestQuery('genres') to get the query value, in a controller $kirby->request()->get('genres')
  • Split by , to get an array of genres and then do your search/filter logic on your pages
  • Depending on how large the list of pages is/how performance heavy the searching/filtering, you might want to cache your response with the genres query as key, so that repeated requests for the same genres can be served directly from your custom cache.

Thanks @distantnative I think this might be the way to go

In a route you can use $this->requestQuery('genres') to get the query value, in a controller $kirby->request()->get('genres')

Ive set a little route up, like this but according to an example in the docs:

<?php

return [
  'debug' => true,
  'routes' => [
    [
      'pattern' => 'comics/(:any)',
      'action'  => function ($uid) {
        // query string, e.g. `https://example.com/comics?genres=comedy,action
        if ($query = get('genres')) {
          return $query;
        }

      }
    ],
  ]
];

But when i hit it directly, i get a Kirby error page. I dont need need a template and blueprint or whatever for `/comics` do I?

If i do this directly in the browser i get an error page

http://localdomain.test/comics?genres=Aliens

Shouldnt it just return the params as plain text in the browser?

comics/(:any) won’t match comics?genres=Aliens, I think.

I see, I based it on this example in the docs:

The URL you tried/posted doesn’t need comics/(:any), it’s just comics.

EDIT: But I think you are right that this example is also weird in that regard.

Ahh i see what you were getting at now. That docs page suggests what i was originally trying should work.

This worked:

<?php

return [
  'debug' => true,
  'routes' => [
    [
      'pattern' => 'comics',
      'action'  => function () {
        // query string, e.g. `https://example.com/comics?genres=comedy,action,classic`
        if ($query = get('genres')) {
          return $query;
        }

      }
    ],
  ]
];

Still running into trouble with this. My Route looks like this:

<?php

return [
  'debug' => true,
  'routes' => [
    [
      'pattern' => 'comics',
      'action'  => function () {
        // query string, e.g. `https://example.com/comics?genres=hunor,action,classic`
        if ($query = get('genres')) {
          // split the genres by comma
          $genres = explode(',', $query); 
          // trim whitespace from each genre
          $genres = array_map('trim', $genres); 
          $filteredPages = kirby()->collection('comics')->filterBy('genre', 'in',  $genres, ',');
          //return the genres as an array
          return $filteredPages;
        }
      }
    ],
  ]
];

It works if i just return the genres, but i when why try to pass that array to filterBy i keep getting an unexpected input error from Kirby

the javascript side thats doing the fetch reques tlooks like this:

function recommendation() {
  swiper.destroy;
  const params = new URLSearchParams({
    genres: comichoices.toString(),
  });

  console.log(params.toString());

  fetch(recRoute + `?${params.toString()}`)
    .then((response) => {
      if (!response.ok) {
        throw new Error("Network response was not ok");
      }
      return response.json();
    })
    .then((data) => {
      let rec = `
        <div> 
          <h2>These are our recommendation&hellip;</h2>
        </div>`;
      console.log(data);
      app.innerHTML = rec;
    })
    .catch((error) => {
      console.error("Fetch error:", error);
    });
}

Getting somewhere if i do this in a page template, it works as expected:

<?php 
$comiclist = $kirby->collection("comics");
$searchTags = ['Hentai', 'Terror'];
$items = $comiclist->filter(function($child) use($searchTags) {
  $tags = $child->genre()->split(',');
  return array_intersect($tags, $searchTags);
});
var_dump($items);
?>

But when i try and do that in a route, im still getting unexpected input eorror from kirby.

<?php

return [
  'debug' => true,
  'routes' => [
    [
      'pattern' => 'comics',
      'action'  => function () {
        // query string, e.g. `https://example.com/comics?genres=hunor,action,classic`
        if ($query = get('genres')) {
          // split the genres by comma
          $genres = explode(',', $query);
          // trim whitespace from each genre
          $genres = array_map('trim', $genres);

          $comiclist = kirby()->collection("comics");

          $searchTags =  $genres;
          
          $items = $comiclist->filter(function ($child) use ($searchTags) {
            $tags = $child->genre()->split(',');
            return array_intersect($tags, $searchTags);
          });

          //return the genres as an array
          return $items;
        }
      }
    ],
  ]
];

If i return the $searchtags i get this as a resonse from the route

["Terror","Hentai"]

But when i feed that in to the filter i get unexpected input. I dont understand why it wors in the template but not in the route.

What am I missing?

That’s because you are not returning an array, but a collection. So you either need to cast to array

return (array) $items;

Or return the data you need.

Ahhh thankyou @texnixe