Routing: find pages by slug and redirect (301)

Hi there!

I’ve updated a website which contains a thousand of pages and would like to make some 301 redirections so the old links point to the new ones.

I’ve created some routes for the main pages which work fine:

[
    'pattern' => 'category/albums',
    'action'  => function() {
        return go('albums', 301);
    }
],
[
    'pattern' => 'category/romans',
    'action'  => function() {
        return go('romans', 301);
    }
],
[
    'pattern' => 'category/bd',
    'action'  => function() {
        return go('bd', 301);
    }
],
[
    'pattern' => 'category/auteurs',
    'action'  => function() {
        return go('auteurs', 301);
    }
],
[
    'pattern' => 'contact',
    'action'  => function() {
        return go('contacts', 301);
    }
],

BUT, I didn’t find a nice solution to redirect all the other pages. Those were hosted at the root of the website like:

Now all the pages are organized in subfolders like:

90% of the page slugs are the same between the old and new website… so my idea was to create a function that check if a new page exists with an old slug and redirect either to this page, or the homepage.

But I could not find a proper way to do this…
Here is what I have tried without success:

[
  other routes listed above
],
[
    'pattern' => '(:any)',
    'action'  => function($id) {
        $page = pages()->findBy('slug', $id);
        return go($page, 301);
    }
]

Any idea what I am doing wrong? :sweat_smile:
Thank you.

You would have to go through the index to make this work which I cannot recommend. If the pages can be in any of those 4 subfolders, I’d go through them manually:

$page = page('albums/' . $id);
if (! $page) = $page = page('romans/' . $id);
// etc.

Or create an array of possible parent pages and loop through them trying to find your page.

Thanks Sonja for your prompt reply.

I tried to make your route work with no success, so I finally decided to wait until Google crawl the new website and update their results…

After all, it’s not a big issue if visitors see a 404 page :rofl:

Could you post your complete route? And how many possible subpages are there? And what is the folder structure of your current site?

Of course!

My content folder looks like this (I’ve translated so it may be clearer to understand the context) :

1_albums (including 326 subpages: /albums/book-title)
2_novels (including 171 subpages: /novels/book-title)
3_comics (including 181 subpages: /comics/book-title)
4_authors (including 469 subpages: /authors/author-name)
5_events (including 10 subpages: /events/event-name)
6_pros (including 5 subpages: /pros/page-title)
7_contacts (no subpage)
8_about (no subpage)
9_legal (no subpage)

There is absolutely NO subpage within a subpage, the maximum depth in the content folder is page/subpage.
I play a lot with params however to filter the children of the 4 first pages. So I have some links that look like: /novels/sortby:date/theme:adventure and /authors/sortby:firstname.

The previous version of the website had the same content, but it was organized differently:
/category/albums
/category/novels
/category/authors
/about-us
/contact
/legal

I have managed to redirect those pages with these simple routes:

[
    'pattern' => 'category/albums',
    'action'  => function() {
        return go('albums', 301);
    }
],
[
    'pattern' => 'category/novels',
    'action'  => function() {
        return go('novels', 301);
    }
],
[
    'pattern' => 'category/comics',
    'action'  => function() {
        return go('comics', 301);
    }
],
[
    'pattern' => 'category/authors',
    'action'  => function() {
        return go('authors', 301);
    }
],
[
    'pattern' => 'contact',
    'action'  => function() {
        return go('contacts', 301);
    }
],
[
    'pattern' => 'about-us',
    'action'  => function() {
        return go('about', 301);
    }
]

The issue is that all the books and authors were stored at the root of the website:
oldwebsite.com/book-title
oldwebsite.com/author-name

And I can’t find a nice way to redirect those to the appropriate link.
I’ve added this route after the ones listed above… which didn’t work:

[
    'pattern' => '(:any)',
    'action'  => function($id) {
        $foundpage = false;
        foreach($kirby->collection('books') as $book){
            if($books->slug() == $id){
                $page = $book->uri();
                $foundpage= true;
            }
        }
        if($foundpage == false){
            foreach($kirby->collection('authors') as $author){
                if($author->slug() == $id){
                    $page = $author->uri();
                    $foundpage= true;
                }
            }
        }
        if($foundpage == false){
            link to 404 page
        } else{
            return go($page, 301);
        }
    }
]

Any idea? :man_shrugging:

First of all, I’d clean up a bit regarding the routes starting with category.

[
    'pattern' => 'category/(:any)',
    'action'  => function($uid) {
        if (in_array($uid, ['albums', 'novels', 'comics', 'authors']) {
            return go($uid, 301);
        } else {
            $this->next();
        }
    }
],

I’ll look into the rest in a minute.

1 Like

Try this:

[
    'pattern' => '(:any)',
    'action'  => function($id) {
        // we check if a first level page exists and return it if true
        if ($page = page($id)) { 
            return $page;
        // otherwise we loop through an array of subpages and try to find the page
        } else {
            $subpages = ['albums', 'novels', 'comics', 'authors'];
            foreach ($subpages as $subpage) {
                if ($page = page($subpage . '/' . $id) {
                    return $page;
                } 
            }
            // if this is not successful, we go to the next route or to the error page
            $this->next(); // or send to error page.
        }
    }
]

I wonder how long it takes to loop through all these pages to find the right one, though.

This code doesn’t make sense, a collection doesn’t have a slug property. You probably mean $book-

You are a life saver Sonja!
This works like a charm!

Thank you very much!

Here, for you: Made with Kirby and <3