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

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.

Builder: 

- 
  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: https://getkirby.com/docs/cookbook/debugging-basics

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.

Interesting!

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).

<?php

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
      go($page->url());
      exit;
    }
    $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.

Digging through the forum, something like this?

if(r::is('POST') && $data = get()) {
    $partners = $page->children()->visible()->filter(function($child) use($keys, $data) {

      foreach($data as $key => $value) {

        if($value && in_array($key, $keys)) {

          if(! $match = in_array($value, $child->$key()->split(','))) {

            return false;
          }
        }
      }

    return $child;

    });
  }

Let me ask the question differently: What do you expect your $data array to look like, can you give me an example?

Yes, sorry for my confusion and thanks for you constant patience.

So since it is a survey, I’d like to get an array that prints both the question and the answer below. Sometimes a question has another question inside it (based on the kind of answer given), so there should be a level 2 of depth, is that possible?

$data = array(
      'q-01' => (get('q-01')),
      'a-01'  => (get('a-01')),
      'q-02'   => (get('q-02')),
      'a-02'     => esc(get('a-02')), // input="text"
    );

YAML is fine. I was just thinking how it would be to have it in JSON (so to handle the indentation / nested level, but maybe unnecessary).

So far I was trying to get acquainted with $_POST and tried something rudimentary like this

$data = array();
    foreach($_POST as $key => $value) {
      $data[] = "$key => $value";
    }

and it was actually printing on a new file, but printing sometimes wrong values.

That’s exactly what I meant, loop through the $_POST array, but you should add the $key as key:

$data = array();
    foreach($_POST as $key => $value) {
      $data[$key] =  esc($value);
    };

However, if $value can be an array as well, you would have to check if it is an array within the foreach loop and then use a nested foreach loop to add those values to the $data array. Also, you don’t want the submit key in it nor probably a token, if you are using one.

1 Like

Great, it’s working now.

I went on and tried to replace the $rules and $messages array from this

$rules = array(
  'q_01'     => array('required'),
  //(...)
 );

$messages = array(
  'q_01' => 'Compila tutte le risposte?',
  //(...)
);

to this

$rules = array();
    foreach($_POST as $key => $value) {
      $rules[$key] = array('required');
    };

    $messages = array();
    foreach($_POST as $key => $value) {
      $messages[$key] = 'Compila tutte le risposte?';
    };

The page, when fully compiled, is “sent”, but what I get back is the error message set in the $messages array.

Instead, I would expect to receive this message when the form did not went through the sending process correctly and or when a field is not being answered? Although in the latter case, the browser is displaying an tooltip alert message saying the field should be filed out.

Well, from the little you gave me above, I can’t possibly make it out. Do you think you could be a bit more generous with your code :wink: ?

Sure! This is my full controller for the survey.

<?php

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

  $survey_item_number = $page->children()->count() + 1;
  //
  $alert = null;

  if(r::is('post') && get('survey_item')) {
    if(!empty(get('website'))) {
      // lets tell the bot that everything is ok
      go($page->url());
      exit;
    }

    $fill_timestamp = array(
      'title'    => 'Questionario #' . $survey_item_number,
      'data'     => date('Y-m-d, H:i:s'),
    );

    $fill_answers = array();
      foreach($_POST as $key => $value) {
        // tell not to pick honeypot values up
        if ($key != 'survey_item' && $key != 'website') {
            $fill_answers[$key] = esc($value);
        }
      };

    $fill = a::merge($fill_timestamp, $fill_answers);

   // $rules = array(
      // 'q_01'     => array('required'),
    // );
    $rules = array();
    foreach($_POST as $key => $value) {
      $rules[$key] = array('required');
    };

    $messages = array();
    foreach($_POST as $key => $value) {
      $messages[$key] = 'Compila tutte le risposte?';
    };
    // 'q_01' => 'Compila tutte le risposte?',

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

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

        // date_default_timezone_set('Europe/Rome');

        $new_survey_item = $page->children()->create(str::slug('questionario-' . $survey_item_number . '-' . date('Y-m-d')) , 'survey_item', $fill);

        $success = 'Il questionario è stato inviato!';
        $fill = array();

      } catch(Exception $e) {
        echo 'Il questionario non è stato inviato: ' . $e->getMessage();
      }
    }
  }

  return compact('alert', 'fill', 'success');
};

I don’t think this is the cause of the issue, but nevertheless it does not make sense to use the invalid helper on $fill. Instead, it should be used on $fill_answers.

Thanks for the tip, I replaced the above code in this way, using $fill_answers instead of $_POST

$fill_answers = array();
      foreach($_POST as $key => $value) {
        // tell not to pick honeypot values up
        if ($key != 'survey_item' && $key != 'website') {
            $fill_answers[$key] = esc($value);
        }
      };

$rules = array();
    foreach($fill_answers as $key => $value) {
      $rules[$key] = array('required');
    };

    $messages = array();
    foreach($fill_answers as $key => $value) {
      $messages[$key] = 'Compila tutte le risposte?';
    };

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

It now works!

I now only need to understand how to print both questions and answers coming from the same page, when making a new survey_item.txt (which gathers the results of a completed, submitted survey form).

<input type="hidden" name="name-field" value="content-field" />

// name-field = name to use when saving this input text as a new field in a page.txt
//content-field = text to use in the new field we are creating