Help with multi-level URL routes?

Hi Kirby folks,

I’ve gotten a basic URL routing working as shown here:

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

				$page = page($uid);

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

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

			}
		],
		[
			'pattern' => 'releases/(:any)',
			'action'  => function($uid) {
				go($uid);
			}
		]
	]

But the issue is that I have two sections under Releases (releases/discography and releases/writings) that each have their own children. When I navigate to those, releases reappears in the URL, when what I want is /discography/$uid or /writings/$uid

If I replace releases/(:any) with releases/(:all) then the URL looks correct, but doesn’t link to content. Trying to maintain legacy URLs as much as possible here, so would love to find a solution for this. I still need to be able to navigate to simply /discography or /writings though.

Many thanks!

You need three routes:

<?php

return [
    'debug' => true,

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

				$page = page($uid);

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

				return $page;

			}
		],
		[
			'pattern' => '(:any)/(:any)',
			'action'  => function($parent, $uid) {

				$page = page($parent.'/'.$uid);

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

				return $page;

			}
		],
		[
			'pattern' => 'releases/(:all)',
			'action'  => function($uid) {
				go($uid);
			}
		]
	]
];

Amazing @texnixe! This works perfectly and makes a lot of sense. Thank you so much!

So I thought this was working perfectly yesterday, but turns out I may actually need 4 routes to make this happen:

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

				$page = page($uid);

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

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

			}
		],
		[
			'pattern' => 'releases/(:any)',
			'action'  => function($uid) {
				go($uid);
			}
		],
		[
			'pattern' => '(:any)/(:any)',
			'action'  => function($parent, $uid) {

				$page = page($parent.'/'.$uid);

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

				return $page;

			}
		],
		[
			'pattern' => 'releases/(:all)',
			'action'  => function($uid) {
				go($uid);
			}
		]
	]

Sorry - one more question here. How do I pass these routed urls to the sitemap?

Best is to create page models with a modified url method. Not only for the sitemap but to get your Urls right in general

I pulled this from elsewhere on the forum:

<?php

class listing_releasesPage extends Page {
  public function url() {
    return $this->site()->url() . '/' . $this->uid();
  }
}

as a starting point and I’m getting the error:

Fatal error : Declaration of listing_releasesPage::url() must be compatible with Kirby\Cms\Page::url($options = NULL): string in ----/site/models/listing_releases.php on line 3

Could you please help me with what I’m doing wrong here? I know this example was K2, but I couldn’t find anything in the docs that talked about this in terms of Page Models. listing_releases is the the template for the child of /releases/ in the route above.

Thank you so much!

If you override a method, if has to be compatible with the parent method

<?php

class listing_releasesPage extends Page {
    public function url($options = null): string
    {
        return  $this->site()->url() . '/' . $this->uid();
    }
}

So I think I have this figured out now, but want to confirm. This works alongside the URL routes in the config?

The Page Model cleans up the URL for links and for the sitemap, and the Routes take care of serving the correct page.

This specific example requires me to remove the underscore, but still seems to work even though the template file itself has an underscore.