Continue with routing after custom route matches

So, I’ve read through a few of the forum posts around custom routing, but I’m not quite getting it clear in my head,.

I have a custom route that looks like this:

        'pattern' => '(:any)',
        'action'  => function ($uri) {

            $originalURI = $uri;

            $uri = cleanURL($uri);

            // if we modified the url, redirect
            if ($originalURI !== $uri) {
                go($uri, 301);
            }

            // otherwise, load the page
            return site()->visit($uri);
        }
    ]

This does what I want it to, namely the URI’s get ‘cleaned’ and then redirected when the URI is “dirty”. (in this case dirty means it has umlauts, a trailing slash, and/or upper case characters.

If however the URI is already “clean,” I’d like normal routing to take place, instead of just showing the page.

The way it is now, things like the pattern plugin’s custom route to /patterns gets clobbered.

I could I suppose rewrite the pattern field in the route to not match on /patterns, but I’d rather have a more all encompassing solution.

The last thing I’ve tried is this, and it kinda works even though I still get an error on the /patterns page.

    [
        'pattern' => '(.*)',
        'action'  => function ($uri) {

            go(cleanURL($uri), 301);

        },
        'filter'  => function ($route) {
            $path = $route->arguments[0];
            if ($path === cleanURL($path) || $path === '/') {
                return false;
            }

            return true;
        },
    ]

Am I just approaching this wrong? This doesn’t feel ‘right’ somehow.

After some additional hacking, I ended up with this:

    [
        'pattern' => '(.*)',
        'action'  => function ($uri) {

            go(cleanURL($uri), 301);
        },
        'filter'  => function ($route) {
            $path = $route->arguments[0];
            $check = new Router();
            foreach ($this->routes() as $customRoute) {
                if ($customRoute['pattern'] !== $route->pattern() && 
                    $check->run($customRoute['pattern'])) {
                    return false;
                }
            }
            if ($path === cleanURL($path) || $path === '/') {
                return false;
            }

            return true;
        },
    ]

Which seems to work, but it seems like a lot of wasted processing.

I’m going to try to rewrite this into three more specific custom routes (one for / removal, one for lowercase, and one for removing umlauts and see if I like that better.

Is there a faster way to check for the existence of other custom routes than what I’ve written?

Why don’t you use a regex pattern that only catches URIs with any of the “dirty” characters?

Because A) I’m not that smart and B) how else would I get to wander aimlessly through the digital countryside when searching for the right answer?. :wink:

I ended up with this:

    [
        // remove trailing slash
        'pattern' => '(.+)/$',
        'action'  => function ($uri) {
            go($uri, 301);
        }
    ],
    [
        'pattern' => '(.*[A-Z].*)',
        'action'  => function ($uri) {
            go(strtolower($uri), 301);
        }
    ],
    [
        'pattern' => '(.*%[c|C]3.*)',
        'action'  => function ($uri) {
            // convert umlauts
            $umlauts = [
                '/%c3%b6/', // ö
                '/%c3%a4/', // ä
                '/%c3%bc/', // ü
                '/%c3%96/', // Ö
                '/%c3%84/', // Ä
                '/%c3%9c/', // Ü
                '/%c3%9f/', // ß
            ];

            $plain = ['oe', 'ae', 'ue', 'oe', 'ae', 'ue', 'ss'];

            go(preg_replace($umlauts, $plain, strtolower($uri)), 301);
        }
    ],

Which results in multiple redirects if there are multiple 'bad" things going on, but really that’s ok since these URLs are phased out already anyway.

On the plus side, I now understand kirby routes a whole lot better.

:joy:

I think with the help of one of these regex pages, it should be possible to come up with the single route. After that, you don’t only know everything about routes but also about regex patterns :wink:

And since you’d probably better handle the trailing slash in the server config or the .htaccess, you would only need a route for upper case or umlauts.