How to use a custom router?

Hi,

I would like to use a route filter on pages with a certain template that, when that page is invisible, only shows the page when the user is logged in (some basic authentication which avoids direct access for “anonymous users” to invisible pages). But I can’t get it to work.

I assume I cannot use filters on the “default method” via c::set('routes',...) ? Is this correct?

So I tried the “use a custom router” way. But I can’t seem to get it to work. This is in my config.php

$router = new Router();
$router->register(array(
    array(
        'pattern' => 'test',
        'method'  => 'GET',
        'action'  => function() {
            error_log("test");
        }
    )
));

When I visit the test page I am redirected (via 302 redirect) to the home page and nothing shows up in my error log.

Any tips on what I am doing wrong?

PS: I’ve read

and I’m using Kirby 2.5.4

Thx!

I think the code has to go into a plugin file. Also, you have to run the router:

<?php
$router = new Router();
$router->register(array(
    array(
        'pattern' => 'test',
        'method'  => 'GET',
        'action'  => function() {
            go('/');
        }
    )
));
if($route = $router->run()) {
  $response = call($route->action(), $route->arguments());
}
1 Like

Hi @texnixe, putting it in a plugin file is a must indeed (it doesn’t run from config.php) AND you have to run it.

What does this do: if($route = $router->run())?

Also; I noticed that I cannot return json from a custom router. Is this expected behaviour?
return response::json(json_encode($data)); works perfectly when added via c::set("routes"), but not when copied into a new router instance. I am getting redirected to the home page again. The 'action' set in the custom router is ran though, as I’ve logged it.

According to the code comments it “iterates through every route to find a matching route”.

Thanks. Have you seen:

Also; I noticed that I cannot return json from a custom router. Is this expected behaviour?
return response::json(json_encode($data)); works perfectly when added via c::set(“routes”), but not when copied into a new router instance. I am getting redirected to the home page again. The ‘action’ set in the custom router is ran though, as I’ve logged it.

Or should I use a route which returns json exclusively in c::set("routes"), but that can’t have filters then?

Yes, I’ve seen it but I have no answer, I’m afraid. I wonder if we really have to run a new router instance to be able to use filters, or if it isn’t possible using kirby()->routes() in a plugin file.

It seems like the “default” kirby router doesn’t support filtering, and the custom router doesn’t support to return json. This feels un-kirby to me :slight_smile:

I hope a solution comes soon for this inconsistency - pretty please.

Custom routers do indeed support JSON responses:

$router = new Router();
$router->register(array(
    array(
        'pattern' => 'test',
        'method'  => 'GET',
        'action'  => function() {
            return response::json(['this' => ['is', 'a', 'test']]);
        }
    )
));

if($route = $router->run()) {
  $response = call($route->action(), $route->arguments());
  die($response);
}

Tested with Kirby 2.5.5-RC1.

You can also use filters with the c::set('routes') approach, just not “global” filters. But adding a filter callback to the route array should definitely work.

Hi @lukasbestle, this indeed seems to work with JSON (on Kirby 2.5.4). But, then again, I have another route registered in the same router that should return a page (not JSON), and then your code doesn’t work for that scenario for me?

Am I doing something wrong?

Code:

$router = new Router();
$router->register(array(
    array(
        'pattern' => [
            '(:any)/news',
            '(:any)/news/(:any)'
        ],
        'method'  => 'GET',
        'filter' => 'auth',
        'action'  => function($lang) {}
    ),
    array(
        'pattern' => 'test',
        'method'  => 'GET',
        'action'  => function() {
            return response::json(['this' => ['is', 'a', 'test']]);
        }
    )
));

if($route = $router->run()) {
  $response = call($route->action(), $route->arguments());
  die($response);
}

FYI: The first route works as expected when I remove die($response); from your code.

– Edit:

I read up here, and now I understand why the first works without die() -> it runs the filter and then the default Kirby CMS router takes over and does what it usually does because the action is empty. Right?

If so, is there any way to call the default router in the action of the first route to avoid die(); ?

– Edit 2:

After thinking a bit further about it; in my scenario it wouldn’t make sense to use a custom router for this as I can do it all in the regular routes. (I thought I couldn’t use alternative methods nor filters in the regular kirby routes — which is obviously wrong as shown here)

I actually don’t see a scenario for using a custom router then? Is there?

Well, custom routers were originally meant to be used outside of the CMS (when using the Toolkit in completely custom projects that don’t involve the core). But many people use custom routers in plugins to have a bit more control over them.

The issue with your example is that you don’t have an auth filter defined in your custom router. And of course your empty action function doesn’t make sense as die($response) expects some kind of response string or object (otherwise it’s basically die(null)).

Edit: I read your question again and now understand what you mean: Custom routers don’t make sense at all if you want to “fall through” to the Kirby router in specific situations. They only make sense if you want to fall through to the Kirby router if no one of your custom routes matches, not in any other situation.

1 Like