Blueprints for Panel forms are usually static YAML files. While this is totally fine for most use cases, there are some situations where you might wish there was a more dynamic way of creating blueprints.
In a Kirby plugin, we can register blueprints inside the blueprints array as key/value pairs, where the key is the name of the blueprint. As values we can either provide the path to a file or we can assign an array or a callback. While an array is static, the callback approach allows us to create blueprint plugins dynamically without sacrificing performance.
This approach works for complete page/user/file blueprints as well as for partial blueprints like tabs, fieldgroups etc. and opens up many new possibilities.
Dynamic blueprints can be risky if they handle unvalidated or untrusted user data. This recipe assumes that all handled content is trusted.
We don't have access to the current page object in the PHP blueprint files, and therefore have to hard-code the pages we want to fetch via PHP. So the possibilities we have with this type of setup are not endless but useful in certain situations.
- A running Kirby Starterkit
- The Pages Display section plugin installed, for the page sections example
Let's start with a new plugin folder called
/site/plugins/. Inside that folder, let's create the obligatory
index.php file, where we will register all blueprints for this recipe.
With programmable blueprints it is now much easier to load different blueprints per user or user role, which is for example useful if you want to hide fields/sections/tabs from certain users/roles.
Let's assume we had multiple user roles, e.g. the default
admin role and other roles like
editor etc., and we wanted to provide a different
site.yml blueprints for each role.
To achieve this, we create a subfolder
/blueprints with two files
Then we register a
site blueprint and assign the two files to use conditionally:
Data::read() to read the
yaml files into an array.
Next we fill the blueprints with some content. For this example, let's keep them simple:
site.admin.yml get's two tabs, one to access all pages of the site, the second for general meta settings which should not be editable by non-admin users.
site.editor.yml file, we don't need tabs and only include the pages overview without the meta data settings:
To check if everything works, delete the
site.yml file from
/site/blueprints. Then log in as
admin. Create a new
editor user role as outlined in the docs, create a new user with this role and log in with this user. You should now see the simplified site view.
A typical situation that has come up multiple times in support are dynamic numbers of pages sections that filter pages by category. Since the number of categories is usually not set in stone from the outset but new categories might be added any time, we cannot possibly set up all sections in advance. In such a use case, being able to create those sections dynamically is a big plus.
For this example we register a pages blueprint called
notes in our
index.php and include the
notes.php file which we have yet to create.
Now create a new subfolder
pages in the
/blueprints folder, and inside the
pages folder the
notes.php file with the following code:
As mentioned in the prerequistes, this example requires the Pages Display plugin.
To use this blueprint, remove the original
As a result, we will end up with as many sections as we have tags (and in this example, there will be only one page per section, because all tags are only used once). For the screenshot I've reassigned the tags, so that the result looks less silly:
Note that we have set
false, because the blueprint would stop working if the page was renamed. If you want to keep the option to change the slug, you can work with a page ID that doesn't change instead.
The same kind of logic will work for dynamic fields, for example if you wanted to create a number of pages fields where users can select one (or more) pages from each parent.
Again, we first register the new blueprint:
And then create a fieldgroup called
multiselects in the given path:
In a page/user/file blueprint, we can now use this field group like this:
When you use language keys in your blueprints to translate field labels etc., these translations are shown based on the selected user language, not based on the currently selected content language.
This example from the docs…
will therefore not switch to the German translation when we switch the content language to German, but when a user selects German as their interface language.
But often, users expect to see the translated option labels when they switch the content language. So, how can we achieve this?
Let's register a new field blueprint in our
Then we create a new file
category.php in the corresponding folder with the following code:
In our page blueprint, we can now replace the category field definition:
When you want to assign allowed templates to a pages section, you have to list them out one by one. Not with our programmatic approach.
For this example, we register a new section blueprint:
/blueprints/sections/notes.php with the following code:
First we fetch all registered page blueprints into
$blueprints and then remove all the unwanted ones from the array. The remaining blueprints we assign to the section's
We can now reuse this section in our templates like normal.
If we need the same set of blueprints for multiple sections, we can return the filtered set from its own blueprint and include it in our PHP pages/sections blueprints:
blueprints.php we now only return an array of blueprints:
And load this array for example in the
We don't have to register
options/blueprints.php, because we cannot use it like a normal extension but have to include it explicitly.
In this last example, we register a tab blueprint.
Let's assume we already had a basic
yml blueprint that we wanted to extend programmatically.
This is our basic blueprint, which we put into the
blueprints/tabs folder for simplicity.
How can we extend this via PHP? In the same folder, let's create the
meta.php file we already registered above, and add the following code:
First, we read the file we want to extend into an array with
Then we change the tab label and add a new field in the
basicMeta section by merging the original fields array with the new
We can now add this tab in our page blueprints:
If you want to add the new field at another position in the array, or remove another item from the original field list, you can achieve this using PHP's array functions.
In this recipe we looked into creating different types of blueprints dynamically, which can be helpful in several use cases. And if you don't like
YAML, it might even become your favorite way of creating blueprints. Despite some limitations, you can still get pretty creative with this approach.
If you have other great ideas how to use this feature, let us know.