Creating page that you never intend to navigate to

I’m surprised I haven’t considered this before, maybe I never addressed it properly. I will have a collection of pages that each hold a single location (name, address, city, etc.). I will then use that collection of pages to allow the user to select a location (pages field type). I’ll never want someone to navigate to one of those location pages - they will only be a collection for internal use. What’s the proper setup here? Do I leave those locations as Drafts? Yeah. Drafts. Right? Will that prevent them from having their own URL?

I usually make them unlisted and then use routes to redirect such URLs to the error page. Same procedure as for subpages that only deliver the content for their parent page.

I see. Thanks.

Drafts would be an option, as they can only be accessed by logged in users (and you don’t have to think about the additionla route), but it somehow doesn’t feel right, at least not to me. Plus you then cannot use the draft state to differentiate published locations from non-published locations in your queries.

But on the positive side the client wouldn’t need to set a state (which clients tend to forget). On this task I’m struggling with setting the proper query to these locations? I’m using a pages field and filtering based on the ‘location’ template, but nothing shows up. Something must be amiss here.

  label: Location
  type: pages
  query: page.index.filterBy('template', 'in', ['location'])
  max: 1

Should be intendedTemplate() if the page has no template. template only works if there is a location.php template in /site/templates, which doesn’t make sense if the page is not accessible.

You could automatically set the state to published using a hook or even a model.

Oh hell… I was thinking of a blueprint as a template. Right. The locations have no template. What’s this new devilry, this intendedTemplate() ? So that assumes the template would be whatever the blueprint is named. Interesting.

It’s a very old devil, this differentiation already existed in Kirby 2.

The intendedTemplate is the template the page would use based on the text file name if there was a template. The blueprint is responsible for setting the content file name.

This has always been a point for confusion because as a user you are told to select a template when creating a page whereas in reality you select the blueprint.

1 Like

True dat. Good to know. Useful.

I know we’re getting way off topic now, but could you point me to some additional info regarding automatically setting a new page state to unlisted or published. Does that hook happen immediately upon adding the page or on save?

Depends on the hook you use. page.create:after hook would publish the page right after creation. page.update:after would publish the page after saving.

There are a few examples somewhere around here. Basically,

return [
    'hooks' => [
        'page.create:after' => function ($page) {

Perfect! As always, thanks for your insight.

The - imo better - alternative to hooks is using a page model, particularly, if you only need this for only a single or few page types:


use Kirby\Cms\Page;
class LocationPage extends Page
   public static function create(array $props) {
        $props['draft'] = false;
        return parent::create($props);

You don’t see me. I hide from code that’s too fancy. My eyes are shut. That means no one can see me.

1 Like


I’m trying and failing at using that intendedTemplate() business in my query for pages. Can you provide an example, say I’m looking for all pages that use the blueprint location.

Should be

page.index.filterBy('intendedTemplate', 'location')

The in filter is not necessary if you only check against one value. But I’m not sure about the page.index part. That only gets the page tree of the current page. So it depends from where you are querying.

That doesn’t work, but I think I might know why. Those location pages are sibling pages. Consider the attached screenshot. I’m on a camp page, but want to get a location from a location page, which is a child of Summer Camps.

That what I thought, so you should probably find the page by direct path or relatively:

page.parent.children.filterBy('intendedTemplate', 'location')

That did it!

But seriously, consider the page model. Then Kirby doesn’t have to do two things (create the page and then change it again in the hook).