Best way to link to a page in a Template

I know this is going to seem like a really dim question. I have a Template and want to link to another page on my website. The page is not a child page and it is not listed. What is the best way to do this?

Relative?

<a href="page-xyz">XYZ</a>

And then will Kirby somehow make it into an absolute link, when the site goes live on a server? I’m working locally at the moment.

Thanks!

You can fetch any page via it’s id with the page() helper, e.g.

<?php if ($p = page('path/to/page')): ?>
  <a href="<?= $p->url() ?>"><?= $p->title() ?></a>
<?php endif ?>

Are there advantages of using this over writing a simple link?

This is a simple link. And you make sure that you are also linking to something that exists.

I don’t understand that code, so unless there are disadvantages that I don’t know about, I’ll stick with the following (and assume Kirby will convert it to absolute):

<a href="page-xyz">XYZ</a>

Thanks

Is that code in a template? This means: is that code written in a .php file inside of /site/templates?

If yes, you’ll need to write it like texnixe wrote above. Kirby won’t have the chance to convert it to the an absolute URL otherwise.

I’ll try to explain the code, starting with your <a href="page-xyz">XYZ</a> and replacing it bit by bit.

The first thing you want to do is get an absolute url. Therefore you get a handle to the page from which you can let Kirby generate the URL:

// Store the target page into the variable $p
$p = page('page-xyz');

$p is now a either null if the page was not found (for any reason), or an object of type Kirby\Cms\Page, alias simply Page. There are lots of stuff you can call on an object (in OOP jargon “instance”) of type Page. You’ll find most of them here: $page | Kirby CMS.
One of those methods is url() which returns you the absolute URL of the given page.

This would print the absolute url of the page stored in $p to the current HTML:

<?php echo $p->url() ?>

You can abbreviate this to (it’s equivalent to):

<?= $p->url() ?>

If you put it into the HTML we started with it would become:

<?php 
// Store the target page into the variable $p
$p = page('page-xyz');
?>

<a href="<?= $p->url() ?>">XYZ</a>

The problem is, however, that $p might be null, like I mentioned above. That means that it does not contain a reference to the page. This could be because you might have deleted the page, for example, or changed its id.
Calling any method on null causes an error (a “Null Pointer Exception”). Therefore we need to make sure our variable $p is not null:

<?php 
// Store the target page into the variable $p
$p = page('page-xyz');

// make sure $p is not null
if($p !== null) {
  // print the absolute page URL
  echo $p->url();
}

The result of an assignment is the assigned value: the result of the operation $p = page('page-xyz') is actually also the page (or null) that gets assigned to $p. Therefore you can write both the assignment and the if clause on the same line:

<?php 
// Store the target page into the variable $p and make sure it's not null
if(($p = page('page-xyz')) !== null) {
  // print the absolute page URL
  echo $p->url();
}

null is a “falsy” value: if you put it into an if clause it’s just like having false. Therefore you don’t really need to check if $p is exactly null, for us it’s enough to know that it isn’t any of the falsy values (like false, null, 0, '', and others), but a “truthy value” (everything else). Therefore it’s enough to write

<?php 
// Store the target page into the variable $p and make sure it's not falsy
if($p = page('page-xyz')) {
  // print the absolute page URL
  echo $p->url();
}

if statements can be written either with the standard syntax like above with curly braces or with the alternative syntax:

<?php 
// Store the target page into the variable $p and make sure it's not falsy
if($p = page('page-xyz')):
  // print the absolute page URL
  echo $p->url();
endif;

This alternative syntax is especially handy when you combine it with HTML, because it integrates better than the curly braces:

<?php if($p = page('page-xyz')): ?>
  <?= $p->url(); ?>
<?php endif; ?>

If we put your HTML back in, it becomes:

<?php if($p = page('page-xyz')): ?>
  <a href="<?= $p->url() ?>">XYZ</a>
<?php endif ?>

@texnixe went one step further and also replaced the XYZ with the actual title of the linked page.

4 Likes

Hey, wow, thanks. That’s a lot to take in!

Quick question could $p be called anything, for example $cupcake ?

Got to dash, but I’ll come back to this in the next few days.

Thanks again

Yes, almost anything (the variable name has to start with either a letter (a-z) or an underscore (_), it can then only contain underscores, letters or numbers.)
So $cupcake is a valid name, $cup_cake2 is valid, $1cupcake is not, $cup-cake is not.

So if the page is a child page, I need to include the path to it, like @texnixe example?

$p = page(‘portfolio/page-xyz’);

I thought an absolute link would start with a slash?:

$p = page(‘/page-xyz’);
$p = page(‘/portfolio/page-xyz’);

Yes.

The Kirby page() helper function takes an “id” as argument, not a URL. The difference is that, while by default they might look the same, they don’t necessarily have to be. For example in multilanguage sites, the URL would often be translated, while the id stays the same. ids in Kirby also don’t start with a slash, they are always absolute.
Passing a URL to page would also be kind of pointless, since in this case the URL is exactly the thing that you don’t know and are trying to get from Kirby.

So by using the Kirby page () helper I can just give the name of the page, and Kirby will find it and generate an absolute link to it? I don’t need to give the absolute route to the page. So it’s not A, but B:

A.

<?php if ($p = page('path/to/example-page')): ?>
  <a href="<?= $p->url() ?>"><?= $p->title() ?></a>
<?php endif ?>

B

<?php if ($p = page('example-page')): ?>
  <a href="<?= $p->url() ?>"><?= $p->title() ?></a>
<?php endif ?>

It’s A. You just don’t have to prefix your absolute path with a “/

So I still need to know the path to the page. I may as well add the slash myself. I’ve read the docs on page helper. Didn’t really understand it. Does the page helper do anything other than add a slash to my path?

the page helper gets you a Page object. With that page object you get access to that page’s content
and also all of this: $page | Kirby CMS

Regarding $p->url() (which is only one of the methods).

Let’s say you install your website into https://wip.example.org/new-website:
page('path/to/example-page')->url() will give you
'https://wip.example.org/new-website/path/to/example-page'

If you translate your website into different languages, it might return:
https://wip.example.org/new-website/de/pfad/zur/beispielseite.

If you then deploy your website to the live environment, it will give you (without you changing anything in your code): https://example.org/path/to/example-page and https://example.org/de/pfad/zur/beispielseite.


Compare this to hardcoding <a href="/path/to/example-page">stuff</a>.
You put it online at https://wip.example.org/new-website; your link no longer works because it will be resolved to https://wip.example.org/path/to/example-page.


So yes, it does more than adding a slash :slight_smile:

BUT WHY?

It’s rather common in professional web development to have different environments, between local, staging, UAT, and production. You don’t want to have different code for every environment.

Right, got you. So the page helper knows where the start of the path should be. So thinking aloud, in your first example if I hard coded ‘/path/to/example-page’ and took my site from being local to installed on a sub-page of my website, then this path wouldn’t take me where I want it to go. But here:

www.someone.com/path/to/example-page

when I would really want it to go here

www.someone.com/new-website/path/to/example-page

Exactly

I think your edit and my post crossed!

## BUT WHY?

It’s rather common in professional web development to have different environments, between local, staging, UAT, and production. You don’t want to have different code for every environment.

That’s brilliant! I get it now. Thank you so much for explaining it.

Also, to be precise… The page helper resolves an id to a Page object.
With that page object you can do much more than just get its URL. An example would be:

<?= $p->title() ?>

we used in the snippets above.

It gets you the title of the page, meaning that if you put that into your link tag, the text in the tag will automatically always show the actual title of the page. So if you had a page that talked about you doing
“consulting work” but, idk, the marketing department tells you that you now need to do “advisor activities” instead, you just change the page title and all links that used $p->title() will then update automatically.

I know this is very dim of me, but I still can’t get the page helper to work.

I only want to use code I can understand. I’d like to use the page helper for the reason you gave about moving my website from local, to a test site on the server, before making it live. Ensuring all the links still work.

On my own website, where I will manually check that the links work, for now, I don’t need the PHP to check that the links works.

So I’m wanting to simply use the page helper to link to pages (because I understand those links will work in different environments). I have the following code, in the footer, on all my pages, both parent and child pages.

<a href="<?= page('newsletter') ?>">Subscribe to newsletter</a>

But when on child pages it takes me to the following (and the error message)

http://localhost:8888/portfolio/newsletter

So I’ve prefixed the link with a slash

<a href="<?= page('/newsletter') ?>">Subscribe to newsletter</a>

and it takes me to the same page

http://localhost:8888/portfolio/newsletter

It has to be <a href="<?= page('newsletter')->url() ?>">Subscribe to newsletter</a>.
page('newsletter') gives you the newsletter Page object.
page('newsletter')->url() gets you the url of the newsletter page.

1 Like