Kirby-uniform: forms in controllers.php ≠ template.php

I’m using Kirby Uniform instead of building a form by hand following this. Say I want to dynamically add a set of forms from the panel:

in template.php I loop over a Kirby Builder object and have printed only the selected forms from a list of available forms, but when using Kirby Uniform it seems you have to define upfront the kind of forms you want to use on the page.

My doubt is: does it make sense to define all available forms listed in the panel also in controller.php, or is better / is there a way to loop over an object also in Kirby Uniform’s controller.php?

If it’s the latter, how would you do that?

Something like this?

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

EDIT

seeing this

ofc reading from the docs, if you use an empty array then Uniform does not save that data as required.

Probably looking elsewhere, to the point of reimplementing this solution.

Though I feel this would be possible also with the Uniform plugin?

1 Like

You can do the same loop in the controller than you do in the template. It might look like this (though I’m not familiar with the builder plugin):

$rules = [];
foreach($page->builder()->toStructure() as $section) {
   $rules[$section->_fieldset()] = $section->rules();
}

$form = new Form($rules);

Sweet, thank you!

I get an error of Illegal Offset Type on this line:

$rules[$section->_fieldset()] = $section->rules();

Should I wrap something with quotes? I’m trying to understand what that error does mean.

My complete controller.php now is:

<?php

use Uniform\Form;

return function ($site, $pages, $page)
{
    $rules = [];
    foreach($page->builder()->toStructure() as $section) {
      $rules[$section->_fieldset()] = $section->rules();
    }

    $form = new Form($rules);

    if (r::is('POST')) {
      $form->logAction([
        'file' => kirby()->roots()->site().'messages.log',
      ]);
    }

    return compact('form');
};

Was your illegal offset error in the panel or on the site itself? I have had issues with the most recent kirby version and the builder plugin, illegal offset included. Try down grading Kirby to 2.5.3 or 2.5.4. Hopefully the builder plugin will be updated soon.

I couldn’t access $page in the same way you do in a front side template in the panel either. Try:

$page->site()->index()

Thread about it here: $site variable not working in Builder plugin snippet

Not sure your setting the data correctly in the array (im fairly new to PHP)

This bit doesnt feel right:

$rules[$section->_fieldset()] = $section->rules();

Try

$rules = [];
foreach($page->builder()->toStructure() as $section) {
  $rules[$section->_fieldset()] = $section->rules();
}
var_dump($rules);

To see whats actually in the array and why the offset is wrong.

Illegal offset type errors occur when you attempt to access an array index using an object or an array as the index key.
SO

So $section->_fieldset() might not return a string as I expected. See what the return value actually is and use something else. Since you already use the builder object to build the form in the template you must take the same property than you use for the name attributes of the form fields.

Thank you both!

If I add $page->site() the Illegal Offset Type error goes away

$rules = [];
  foreach($page->site()->builder()->toStructure() as $section) {
    $rules[$section->_fieldset()] = $section->rules();
  }

var_dump($rules);

but var dump spits back an empty array

array(0) { }

I think the controller cannot access the actual data I use in the kirby builder loop in template.php, meaning the snippet?

<?php foreach($page->builder()->toStructure() as $section): ?>
  <?php snippet($section->_fieldset(), array('data' => $section)) ?>
<?php endforeach ?>

If I try to add the snippet part like so

$rules = [];
  foreach($page->builder()->toStructure() as $section) {
    $rules[$section->_fieldset(), array('data' => $section)] = $section->rules();
}
var_dump($rules);

I get a syntax error due to the , to add the snippet part:

syntax error, unexpected ',', expecting ']'

i think u need to give that a little more… as i said, my PHP is pretty green, but with that your basically accessing $site and since theres no builder field on your site options page, the array is getting nothing. I think you need to be more specific about where the data is your are trying to populate the array is.

Oh, silly me sure :stuck_out_tongue:

I am not sure what your use case is, but the way i found pages and pulled into the panel was this:

$page->site()->index()->findBy('uid', $data->teasercontent())

This was inside one of my builder snippets, and the builder print contained a drop down (teasercontent) of all pages so i could create a teaser hero panel just by picking a page. youll probably wont some way similar to feed the page through that has the data you want to use.

My snippet call looks like this:

<?php
$sections = $page->builder()->toStructure();
foreach($sections as $section):
  $index = $sections->indexOf($section);
  $next = $index + 1;
?>
    <div class="builder-block" data-scroll-index="<?= $index ?>">
        <?php snippet('builder/' . $section->_fieldset(), array('data' => $section, 'nextpanel' => $next)) ?>
    </div>

<?php endforeach ?>

Just to show you can pass extra bits data through, the above is adding unique html ID’s to each builder entry on the page.

Also, i dont know if you are aware, but controllers only work on the page that controller belongs too. To have it work on any page, you need to rename the controller to site.php. For example, i have a newsletter signup form on every page. Rather then put the Uniform controller code in duplicated controllers, you can put in a controller called site.php. This might be contributing to your problem.

@jimbobrjames thanks for all the tips!

This is confusing me a lot.

I tried to get the correct path w/

$page->site()->index()->filterBy('intendedTemplate', 'registration-form');

and compared it using gettype() to the $page object of the template I am using kirby builder:

same path, same type (object).

But controllers.php was telling me I was getting a Null object.

So then I tried to use this

$page->site()->index()->findBy('uid', 'name-of-page');

which gives me an objectarray (?), and once I use it inside the Uniform controllers.php loop I get the same Offset Illegal Type error as in the beginning.

This makes me think the problem is not using $page or $page->site()-> etc after all?

I am also getting back to PHP after a while and am not very sharp lately… will keep pocking at it.

All of the $page->$buillder()->toStructure() as $section fields are not string but object.

:thinking:

That’s because unless I call them and they get printed, the way they are stored is in the object form? How do I convert them to being strings?

Its because its an object not a collection: Reading external JSON

tl;dr … create your own array from the collection data THEN do stuff with it:

$rules = array ();

$rules[] = array(
      'YourThing' => $data->whatever();
   );

Obviously i’ve made that up but hopefully you get the idea.

1 Like

I had to use value…ie… $page->title()->value()

1 Like

OK so I convert the kirby-builder object to an array.

$rules = $page->builder()->toStructure()->toArray();

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

This is at last saving the filled out fields to a text.log, correctly!

Only, only the last field in the form, which is a type=number field, gets skipped over, and instead the csrf_token gets saved?

Maybe it’s due to esc($value)?

again… flakey PHP knowledge…but its a number isn’t it, not a string, and your treating it as a string? Shouldnt you first check the value is string or a number then treat it appropriately? If you changed that number field to a text field, i bet you get a value.

What does var_dump() tell you?

strval($value); converts an int to a string. Or this.

Try:

$rules = $page->builder()->toStructure()->toArray();

  $rules = array();
    foreach($_POST as $key => $value) {
      if (is_numeric($value)) {
      $rules[$key] = strval($value);
      } else {
      $rules[$key] = esc($value);
      }
 }
;

Strange thing: I already use a couple of field numbers in the form, and they work flawlessly.

Only this last one before the submit button gets completely ignored. Still looking into it and will post an update.

On the other side, this is the complete controller.php for the Uniform Plugin to work w/ Kirby Builder.

I added a custom action to save the form as a kirby subpage.

<?php

use Uniform\Form;

return function ($site, $pages, $page)
{
    date_default_timezone_set('Europe/Rome');
    
    $rules = $page->builder()->toStructure()->toArray();

    $rules = array();
    foreach($_POST as $key => $value) {
      if ($key != 'csrf_token' && $key != 'website') {
        $rules[$key] = esc($value);
      }
    };
    
    $form = new Form($rules);
    if (r::is('POST')) {

      $new_registrationform = $page->grandChildren()->filterBy('intendedTemplate', 'registration-form')->create(str::slug('reg' . '-' . date('Ymd:His')) , 'registration-form', $rules);

    }

    return compact('form');
};

Is the field that is getting ignored properly named? No typos in controller? No typos in the HTML?

Also, its a better idea to set timezone in php.ini if you have access to it.