Shared Controllers
A simple example
We'll use the <title>
and <meta name="description">
tags as an example. The easiest way to add those two tags to our site would be to add them directly into the header.php
snippet. Something like this:
<!DOCTYPE html>
<html>
<head>
<!-- The title tag shows the title of our site and the title of the current page -->
<title><?= $site->title() ?> | <?= $page->title() ?></title>
<!-- The meta description shows an excerpt of the main text -->
<meta name="description" content="<?= $page->text()->excerpt(120) ?>">
</head>
That's a perfectly fine and reasonable solution. But what if we want to show a slightly different title for the homepage for example?
<!DOCTYPE html>
<html>
<head>
<!-- The title tag shows the title of our site and the title of the current page -->
<title><?php e($page->isHomePage() , $site->title() . "|" . $site->tagline() , $site->title() . "|" . $page->title()) ?></title>
<!-- The meta description shows an excerpt of the main text -->
<meta name="description" content="<?= $page->text()->excerpt(120) ?>">Â Â
</head>
This is already starting to look ugly and things will only get messier if we start adding more logic or other tags. It then makes sense to move the logic from the snippet into a controller.
Let's do that.
site
controller
The first thing we need to do is set up our site controller. This controller's data is shared among all pages - ideal for us to provide the title to all pages. To do that we'll add a site.php
to our site/controllers/
folders:
<?php
return function ($page, $pages, $site, $kirby) {
# Fetch and store the content for the title tag and the meta description
$titleTag = $site->title() . " | " . $page->title();
$metaDescription = $page->text()->excerpt(120);
# Return an array containing the data that we want to pass to the template
return compact('titleTag' , 'metaDescription');
};
Now that the logic for our data has been moved inside the controller, we can simplify the header.php
snippet.
<!DOCTYPE html>
<html>
<head>
<!-- The title tag we show the title of our site and the title of the current page -->
<title><?= $titleTag ?></title>
<!-- The meta description shows an excerpt of the main text -->
<meta name="description" content="<?= $metaDescription ?>">
</head>
<body>
Excellent, this is already looking a lot cleaner. Now we need to implement the different logic to create the <title>
tag for the homepage. To do that we'll use a home.php
controller:
<?php
return function ($page, $pages, $site, $kirby) {
 # Store the content for the different title tag
$titleTag = $site->title() . " | " . $site->tagline();
 # Return the array containing the data that we want to pass to the template
return compact('titleTag');
};
When the data from the site controller and from the home controller is merged, we will end up with the custom $titleTag
for our home page, but still with the default $metaDescription
from the site controller.
This is a great way for us to define general data in the site controller and custom data in each page's specific controller.
A more complex example
But what if we have data that should be shared between some templates, but not all templates. Here the site controller could not be the right tool as it adds the data to all pages.
For example, in a blog you could have different templates/controllers for different blog post types…
controllers
- post.php
- post-video.php
- post-image.php
…but you want to leverage to share the data of the post.php
controller with the other controllers.
A shared controller
This is where the concept of the shared controller becomes useful. Rather than copy and pasting the same code, we'll first use the $kirby->controller()
method to get the data from the post.php
controller and store it in a variable.
# Grab the data from the `post` controller
$post = $kirby->controller('post' , compact('page', 'pages', 'site', 'kirby'));
More info on the controller()
method can be found in the docs.
Then we'll gather our custom data that we need specifically just for the post-video
template:
# Custom content for the video post
$video = $page->videourl();
And finally, we'll merge the data and return it as an array that will then get passed to the template
# Return the array containing the data that we want to pass to the template
return A::merge($post , compact('video'));
Be careful, when merging the two arrays, to pass the $post
array as the first parameter to the A::merge()
function. This is important because otherwise your new data won't overwrite the content coming from the shared controller.
Final shared controller
The final post-video.php
controller will look like this:
<?php
return function ($page, $pages, $site, $kirby) {
 # Grab the data from the post.php controller
$post = $kirby->controller('post' , compact('page', 'pages', 'site', 'kirby'));
 # Custom content for the video post
$video = $page->videourl();
 # Return the array containing the data that we want to pass to the template
return A::merge($post , compact('video'));
};
And that's it. We now have a place to store all the data that is shared among all our various post templates and we have a way, using controllers, to overwrite what needs to be overwritten on a template-by-template basis.