Use route to filter out part of directory address in url

I’m learning about the route function, and I wonder if it’s possible to apply one that from something like this

kirby.dev/search/tags:test

redirects to

kirby.dev/search/test

hence, filtering out the tags: part?

As of now I have

c::set('routes', array(
  array(
    'pattern' => 'search/tags:',
    'action'  => function() {
      go('search/'); // put something here after `search/`
    }
  )
));

but of course it does not change anything.

Is it technically doable?

Not tested, but I think it should be the other way round:

c::set('routes', array(
  array(
    'pattern' => 'search/(:any)',
    'action'  => function() {
      // return the results here
    }
  )
));

If I set it like that, it does not do anything as well.

Maybe I should set a variable that iterates over each tag in the search page and place it like this?

c::set('routes', array(
  array(
    'pattern' => 'search/(:any):',
    'action'  => function() {
     
     $tag = page('search')->children()->visible()->pluck('search', ',', true);

     go('search/' . $tag);
    }
  )
));

I think, the point here is not to reroute, but return the result:

c::set('routes', array(
  array(
    'pattern' => 'cerca/(:any)',
    'action'  => function($tag) {
      return page('cerca') . '/tag:' . $tag;
    }
  )
));

But this is just guessing, I don’t know if it really works like this :thinking:

Yes I think you’re right in terms of approach, but it’s not working yet.

Will peruse this direction and report back! Thanks.

Okay, I’m trying this routing function again.

Currently:

http://artoteca.dev/catalogo/technique:arte-astratta

Desired output

http://artoteca.dev/catalogo/technique/arte-astratta

This is what I have so far:

  1. match anything after http://artoteca.dev/catalogo with a regex

  2. return new url structure

  3. it should change based on the site language, so technique should actually be something else for both the default language (italian) and the alternative language (german). This part is still missing below.

$collection = site()->findBy('intendedTemplate', 'collection');

c::set('routes', array(
  array(
    'pattern' => $collection . '/' . '((technique:*)(-*)(.*)([a-z]))',
    'action'  => function($tag) {
      return page($collection) . '/technique' . '/' . $tag;

    }
  )
));

This route does not produce anything as of now, but the matching should be correct.

Is the return missing something?

1 Like

I would do that the other way round: return a filtered collection from the route.

Then you can use the $args parameter in your controller.

Thank you for pointing me to these resources.

So, I updated the router to

$collection = site()->findBy('intendedTemplate', 'collection');

c::set('routes', array(
  array(
    'pattern' => $collection . '/' . '((technique:*)(-*)(.*)([a-z]))',
    'action'  => function($tagfilter, $tag) {
      $data = array('tagfilter' => $tagfilter, 'tag'=>$tag);
      return array($collection, $data);

    }
  )
));

And set $args to the controller. Then I am a bit clueless but, I should iterate:

  1. $tagfilter to fetch the right value based on site->language();
  2. but what about $tag? not sure if I need to make a variable for it, since I only need to transform the colon to a forward slash

I don’t quite understand why you are using the pattern of the URL you don’t want to use. If you want to use a pattern like

http://artoteca.dev/catalogo/technique/arte-astratta

then you should query that in your pattern, i.e.

'pattern' => 'catalogo/(:any)/(:any)'

I am not using that url pattern, the regex is querying

technique:arte-astratta

from

http://artoteca.dev/catalogo/technique:arte-astratta

So I now added this in the controller

# filter artworks by tag
if (isset($args['technique'])):
   $artworks = $artworks->filterBy('technique' , '==' , $args['technique']);
endif;

Yes, I know, but why? I thought you wanted to use a URL without the colon? Otherwise you would not even need the route…

Yeah, I think then that I am still highly confused by the logic of routers. I now see what you mean.

Cool, now if I manually go to

artoteca.dev/catalogo/technique/arte-concettuale

the page does not throw and error, but simply shows a page with not result, meaning the tag-filtering is not being triggered. Which is a step forward.

$data = array('technique' => $tagfilter, 'tag'=>$tag);

in this array to what other real values do the two variables need to refer to? Or do they refer to the url-structure I am mapping?

Suppose your route looks like this:

c::set('routes', array(
  array(
    'pattern' => 'catalogo/technique/(:any)',
    'action'  => function($tag) {
      $data = array('technique' => $tag);
      return array(page('catalogo'), $data);

    }
  )
));

then you only need the value $tag. You only return that in $data.

if (isset($args['technique'])):
   $artworks = $artworks->filterBy('technique, $args['technique']);
endif;

Unless you want to make that more dynamic, i.e. allow filtering by different parameters.

1 Like

Thanks for the clear explanation!

It still is not wokring (no url redirect and the filter does not return any result), but at least it was useful to understand the logic behind routing.

Try this (should work if I got all the variables right)

Route:

c::set('routes', array(
  array(
    'pattern' => 'catalogo/technique/(:any)',
    'action'  => function($tag) {
      $data = array('technique' => $tag);
      return array(page('catalogo'), $data);

    }
  )
));

Controller:

<?php
return function($site, $pages, $page, $args) {

  $artworks = $page->children()->visible();


  if(isset($args['technique'])) {
    $artworks = $artworks->filterBy('technique', $args['technique'], ',');
  }
  return compact(
    'artworks'
  );
};

Thank you again for your help, still no luck on my side.

I suspected there was a problem in using the same $artworks variable multiple times in the controller, but I tried to make a new one only for the router and instead of showing no result, it displayed all results.

Below my full controller:

<?php
return function($site, $pages, $page, $args) {

  //++ artworks
  $artworks = $page->children('template', 'artworks')->children()->visible();

  //++ technique
  $techniques = $artworks->pluck('technique', ',', true);
  asort($techniques);

  if(!empty(param('technique'))) {
    $artworks = $artworks->filter(function($result) use($techniques) {
      // split tags and convert to lowercase
      $techniques = array_map('str::slug', $result->technique()->split(','));
      // return the page if the filter is in the array
      if(in_array(str::slug(param('technique')), $techniques)) return true;
    });
  }

  $p_technique = kirby()->request()->params()->technique();

  //++ route: filter artworks by tag
  if(isset($args['technique'])) {
    $artworks = $artworks->filterBy('technique', $args['technique'], ',');
  }

  //++ author
  $authors = $page->children()->findBy('intendedTemplate', 'authors')->children()->visible();
  $p_author = kirby()->request()->params()->author();

  // search
  // + + + +

  // return all children if nothing is selected
  $query = get('q');

  if($query) {
    $artworks = $artworks->search($query, 'title|text');
  }

  $artworks = $artworks->paginate(20);
  $pagination = $artworks->pagination();

  // return
  // + + + +

  return compact ('artworks', 'techniques', 'p_technique', 'authors', 'p_author', 'query', 'pagination');
};
1 Like