Using kirby builder to add new radio-button options from the panel?


as the title posits, is it possible to use kirby builder (or eventually kirby module) to interactively add new entries as options of a radio-button field from the panel?

I need to build a big survey page with some common modules (some questions are yes/no, some let you pick between, say, 5 options, some other are open questions where you have to pick an option and then motivate it by writing yourself an answer, etc).

I was thinking if it was possible to build the survey through kirby-builder instead of hard-coding every question myself, plus being able to use kirby-sortable to re-order modules (?)

But as far as I can see, you can only fetch options from other pages or fields if you don’t want to set the options to a radio button field. Is that correct?

Any workaround?

EDIT: reading about Custom List Field.

There are several options:

Thank you!

Is it possible to modularise the whole process in a way that I use the Controlled List plugin within kirby builder or kirby module?

I have no experience with neither of them.

In this way I could selectively use ad-hoc snippets to build, in the final instance, the html page way faster and with more control in case of changing content.

Yes, you can use these fields like any other fields.

I have just used the Builder plugin for this site (work-in-progress):

Is the survey there close to what you want to achieve? If so, I can provide some tips for how I set up my builder fields.


The survey I am building is more or less presenting the same structure as yours.

I’d be very curious to see how you achieved that! If you can share any insight or code sample, I’d appreciated that.

UPDATE: So I basically wrote most of the survey page by hand to try to figure what are the variables that I can encapsulate in snippets, and in general to figure out the overall structure of the template.

Kirby Builder would be perfect, but this time I also need its only limitation—being able to nest structured field within structured field. It cannot do it… yet.

In my case it’s because I am using <fieldset> to split and hold together groups of questions and answers, and then being able to number correctly <input>'s id and name field.

Therefore I think the only way to achieve this is to go with Kirby Modules and Kirby Sortable. Kirby Modules has the big drawback of not being able to show what’s inside each page, but well done labelling might help in this.

Kirby Modules + Kirby Sortable + Kirby Builder.

This is how mine looks…

The blueprint for the radiofield fieldset looks like this:

        label: Radio buttons
            type: text
            label: ID
            type: text
            label: Label
            type: textarea
            label: Options
            help: 'value|Label|Message'

In my template, I loop through each builder entry, determine which fieldset type it is, and output a snippet based on the fieldset type.

Thanks! Really useful to have an idea of how to extend it.

Now I made it work by using Kirby Module + Kirby Sortable + Kirby Builder, and it’s really great. Amazing.

I have encountered a problem while making a custom preview file for Kirby Sortable. I print out the builder structured object, and I choose which kind of builder snippet file to use to create the preview in the panel.

<?php foreach($module->builder()->toStructure() as $section): ?>
  <?php snippet('sections/preview', array('data' => $section)) ?>
<?php endforeach ?>

All the above works. The only thing I could not set is, how to tell the builder to output only the first object of its multi-object collection.

I tried using this approach by @texnixe (Issue with filtering data using structures)

$reviews = $page->children()->visible()->filter(function($p) {       
  return $p->scores()->toStructure()->filterBy('platform', '==', urldecode(param('platform')))->count() > 0;

and turned it into

  $preview = $module->filter(function($p) {
    return $p->builder()->toStructure()->first();

but it does not print out anything.

What is $module in your code above? Are you trying to filter a single page?

The $module above is the special object created by kirby-modules.

What I am trying to achieve is to create a preview template for kirby-sortable (for kirby panel), in which I am printing out only the first element of a kirby-builder object.

With the current syntax, kirby-builder prints out the whole structured field. I’d like to be able to print out the first element of the field, no matter the fieldset type.

The problem arises because I am filtering a nested field I think, that’s why I thought that using a callback would have helped.


  text: WWWWWW
  _fieldset: question
  text: Ti consideri artista?
  _fieldset: question
  text: whoa
  _fieldset: answer_dot

That still does not tell me what type of object $module is. All I can tell you is that you can only filter a collection and I have my doubts that $module is a collection in this case. Shouldn’t it be something like $module->builder()->toStructure()->first()?

Yes, it works :smiley:

Thank you.

To be more precise when I have to explain things: which options do I have when replying to your question of “which kind of object $module is?”.

A var_dump(), in this case var_dump($module) or in Kirby language dump($module) (but beware, you can easily get addicted to that and trying to use dump() in non-Kirby projects then throws an error) can be illuminating :wink:

Oh, and there’s also a little debugging recipe:

Oh nice, my strategy so far was to echo throughout a template in order to check if the code was working as expected, var_dump() adda an extra level of granularity.

Last step to finish a big survey page created from the panel.

I followed this guide to create pages from the frontend, all good.

The only difference now, is that I don’t know in advance the number and order of those fields, since I create them using Kirby Modules and Kirby Builder from the panel.

If I make my $collection like this

<?php foreach($collection as $section): ?>
  <?php snippet('sections/' . $section->_fieldset(), array('data' => $section, 'module_num' => $module_num, 'section_num' => $section_num)) ?>
<?php endforeach ?>

How can I pass the above variable $section, to the controller which create new sub-pages once the form is being sent?

$fill = array(
  // I need to keep this first two items
  'title'    => 'Questionario #' . $survey_item_number,
  'data'     => date('Y-m-d, H:i:s'),
  'q_01' => esc(get('q_01')),

You could use a hidden field to send the section with the form.


But I don’t quite fully understand it.

Like, why not sending the builder field (used by Kirby Builder) for instance?

Asking simply because I am confused. I am overseeing something probably.

Well, maybe I misunderstood what you wanted to achieve.Why do you need the $section variable in the controller?

I am sending all the filled-in input forms to kirby and save each of them to a new text-file in a subpage.

I want to replace all the items in the $data array, by looping over the $section object from Kirby Structure, which is what I used to create the input forms (Numbering Kirby Modules and Kirby Builder structure field).

So iterating over the IDs and names I built from the numeration of each element in the structure field, and put them in the array, if my understanding of how the input form works is correct (first time trying this).


return function($site, $pages, $page) {

  $alert = null;

  if(r::is('post') && get('register')) {
    if(!empty(get('website'))) {
      // lets tell the bot that everything is ok
    $data = array(
      'firstname' => esc(get('firstname')),
      'lastname'  => esc(get('lastname')),
      'company'   => esc(get('company')),
      'email'     => esc(get('email')),
      'message'   => esc(get('message'))

    $rules = array(
      'firstname' => array('required'),
      'lastname'  => array('required'),
      'email'     => array('required', 'email'),
    $messages = array(
      'firstname' => 'Please enter a valid first name',
      'lastname'  => 'Please enter a valid last name',
      'email'     => 'Please enter a valid email address',

    // some of the data is invalid
    if($invalid = invalid($data, $rules, $messages)) {
      $alert = $invalid;
    } else {

      // everything is ok, let's try to create a new registration
      try {

        $newRegistration = $page->find('registrations')->children()->create(str::slug($data['lastname'] . '-' . $data['firstname'] . '-' . time()) , 'register', $data);

        $success = 'Your registration was successful';
        $data = array();

      } catch(Exception $e) {
        echo 'Your registration failed: ' . $e->getMessage();

  return compact('alert', 'data', 'success');

I see, you want to get the key/value pairs from the forms to build the data array, don’t you? Then all you need is to build it from the $_POST array (get()), because it contains all these keys/values.