How to filter a global route to exclude some specific paths

I’m having trouble to filter a route.

I have set up an architecture that requires some folder names to be omitted, so I have a global route which catches every path. But I want to skip this route in case in encounters some specific UIDs, for my sitemap and some other specific route that are currently not working as they don’t get to run. It looks like this :

kirby()->routes(array(
    array(
    	'pattern' => '(:any)',
    	'action'  => function($uid) {
        	$page = page($uid);

        	if(!$page) $page = page('database/' . $uid);
        	if(!$page) $page = site()->errorPage();

        	return site()->visit($page);
    	},
    	'filter'  => function($route) {
    		if (?? == 'sitemap.xml') { return false; }
    	},
 	),
));

My issue is with the ?? part, I don’t get how to retrieve the $uid from within the filter, don’t find any doc about the $route array and as it’s running within the filter I don’t know how to dump/print it somewhere to look into it :thinking:

I saw this behavior has been implemented though, from this post. @jenstornell, @lukasbestle would you have some tips ?

Thanks !

This seems to work:

kirby()->routes([
    [
    	'pattern' => '(:any)',
    	'action'  => function($uid) {
        	$page = page($uid);

        	if(!$page) $page = page('database/' . $uid);
        	if(!$page) $page = site()->errorPage();
        	return site()->visit($page);
    	},
    	'filter'  => function($route) {  
    	  if(in_array('sitemap.xml', $route->arguments())) { return false; }
    	},
 	]
]);

Filters are not really meant to restrict the pattern of the route, their main use-case is authentication and other “environment checks”.

The best way would be defining the pattern in a way that excludes the sitemap in the first place:

kirby()->routes(array(
    array(
    	'pattern' => '^(?!sitemap\.xml$)(.*)$',
    	'action'  => function($uid) {
        	$page = page($uid);

        	if(!$page) $page = page('database/' . $uid);
        	if(!$page) $page = site()->errorPage();

        	return site()->visit($page);
    	},
 	),
));

Looks complicated, but all it does is to check if the URI is not equal to sitemap.xml. The pattern matches for any other URI.

Of course this gets pretty messy once you need to exclude multiple URIs. In this case, @texnixe’s solution is cleaner and the better solution.

Yes I did this (well, yours is cleaner) in the first place but as there are some other paths to deal with I wasn’t brave enough to keep messing with the patterns, and the post mentioned above got me confused about whether it had been added to the filter behaviour.

Crystal clear. Thanks to both of you !

I often did like you with the pattern (:any) or (:all), but nowdays I try to avoid it if possible. To me it caused all kinds of unexpected side effects, so it feelt a bit dangerous.

I usually make small sites and for those I can often do something like this:

$block = [];
foreach(site()->children()->filterBy('intendedTemplate', 'category-group') as $item) {
    $block[] = $item->id();
}

kirby()->routes([
    [
        'pattern' => $block,
        'action' => function() {
            return site()->visit('error');
        }
    ],
]);

The pattern of the above generates something like this:

'pattern' => ['categories', 'brands', 'companies']

I’ve taken the code right out of my latest project and if it matches the pattern it will just show the error page. I do that because categories, brands and companies serves as placeholders for the real content in the panel.

Anyway, with this approach, first you generate the pattern dynamically and then you use it.

Because it will get some content on each page load, it could possibly be a bit slower (I did not notice anything). If it’s a problem the Kirby cache may help or https://github.com/jenstornell/kirby-time-cache.

1 Like