Form controller shall receive data from structure field

Hi there,

my form controller shall receive the input for the fields from a structure field.

I am working with this: https://getkirby.com/docs/cookbook/forms/basic-contact-form):

Basically I want to replace this in the controller:

$data = [
    'name'  => get('name'),
    'email' => get('email'),
    'text'  => get('text')
];

with something like this:

    foreach(page()->form()->toStructure() as $item):
        $data[] = $item->name();
    endforeach;

It seems to be a problem with the array, but I don’t get it, when I submit the form I get the error: Illegal offset type in isset or empty

Any idea?

Two issues:

foreach(page()->form()->toStructure() as $item):
    $data['name'] = $item->name()->value();
    // etc. for other key/value pairs
endforeach;

Note that this will create a nested array.

But I’m not sure what you want to achieve here.

Well my goal is that my client can add form fields in the panel (and nobody has to deal with the controller.php). I know I need to take care of $rules and $messages (in controller) too and there plugin solutions but I wanted (to try) to do it myself.

The error (Illegal offset type in isset or empty) is gone, but still get the The form could not be sent - need to further investigate.

Thanks for now.

Maybe you can post the complete code, including Panel blueprint, controller and form.

Because $data is supposed to be the data that comes in through the form, so it doesn’t really make sense to fill that array with data from the content file?

On a side note: Are you aware of the formbuilder plugin: https://getkirby.com/plugins/cre8ivclick/formbuilder

Right, $data shouldn’t be filled. But the fields in it need to be dynamic depending on what comes from the panel via page()->form()->toStructure().

And make that dynamic:

   $data = [    
        'name'  => get('name'),
        'email' => get('email'),
        'text'  => get('text'),
    ];

controller:

<?php

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

    $alert = null;

    if($kirby->request()->is('POST') && get('submit')) {

        // check the honeypot
        if(empty(get('website')) === false) {
            go($page->url());
            exit;
        }

        foreach(page()->form()->toStructure() as $item):
            $data['name'] = $item->name()->value();
            // etc. for other key/value pairs
        endforeach;
        
#        $data = [    
#            'name'  => get('name'),
#            'email' => get('email'),
#            'text'  => get('text'),
#        ];

        $rules = [
            'name'  => ['required', 'min' => 3],
            'email' => ['required', 'email'],
            'text'  => ['required', 'min' => 3, 'max' => 3000],
        ];

        $messages = [
            'name'  => 'Please enter a valid name',
            'email' => 'Please enter a valid email address',
            'text'  => 'Please enter a text between 3 and 3000 characters',
        ];

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

            // the data is fine, let's send the email
        } else {
            try {

                $kirby->email([
                    'template' => 'form',
                    'from'     => 'yourcontactform@yourcompany.com',
                    'replyTo'  => $data['email'],
                    'to'       => 'you@yourcompany.com',
                    'subject'  => esc($data['name']) . ' sent you a message from your contact form',

                    'data'     => [
                        'text'   => esc($data['text']),
                        'name' => esc($data['name']),
                        'email' => esc($data['email']),
                    ]
                ]);

            } catch (Exception $error) {
                $alert['error'] = "<br>The form could not be sent";
            }

            // no exception occured, let's send a success message
            if (empty($alert) === true) {
      
                $success = '<p class="center pri">Ihre Anfrage wurde erfolgreich versendet. Vielen Dank!<br>Wir melden uns bei Ihnen umgehend!</p>';

                $data = [];
            }
        }
    }

    return [
        'alert'   => $alert,
        'data'    => $data ?? false,
        'success' => $success ?? false,
    ];
};

blueprint:

title: form
columns:
  main:
    width: 2/3
    fields:
      text:
        type: textarea
      form:
        type: structure
        fields:
          name:
            type: text
            width: 1/4
          type:
            type: select
            default: text
            width: 1/4
            options:
              text: text
              email: email
              textarea: textarea
              selection: selection
          text:
            type: text
          option:
            type: structure
            fields:
              name:
                type: text
                width: 1/4
          image:
            type: files
            uploads: 
            parent: site
            template: files-upload

form:

    <?php if($success): ?>
    <div class="alert success">
        <p><?= $success ?></p>
    </div>
    <?php else: ?>
    <?php if (isset($alert['error'])): ?>
        <div><?= $alert['error'] ?></div>
    <?php endif ?>

    <form method="post" action="<?= $page->url() ?>" style="margin: auto;">

    <div class="honeypot">
        <label for="website">Website <abbr title="required">*</abbr></label>
        <input type="website" id="website" name="website">
    </div>

        <?php foreach($page->form()->toStructure() as $item): ?>

            <?php if($item->type() == 'text'): ?>

                <div class="form-element <?= $item->name()->value() ?>">
                    <label for="<?= $item->name()->value() ?>">
                    <?= $item->name()->value() ?> <abbr title="required">*</abbr>
                    </label>
                    <input type="<?= $item->type() ?>" id="<?= $item->name()->value() ?>" name="<?= $item->name()->value() ?>" value="<?= $data[$item->name()->value()] ?? '' ?>" required>
                    <?= isset($alert[$item->name()->value()]) ? '<span class="alert error">' . html($alert[$item->name()->value()]) . '</span>' : '' ?>
                </div>

            <?php elseif($item->type() == 'email'): ?>

                <div class="form-element <?= $item->name()->value() ?>">
                    <label for="<?= $item->name()->value() ?>">
                    <?= $item->name()->value() ?> <abbr title="required">*</abbr>
                    </label>
                    <input type="<?= $item->type() ?>" id="<?= $item->name()->value() ?>" name="<?= $item->name()->value() ?>" value="<?= $data[$item->name()->value()] ?? '' ?>" required>
                    <?= isset($alert[$item->name()->value()]) ? '<span class="alert error">' . html($alert[$item->name()->value()]) . '</span>' : '' ?>
                </div>

            <?php elseif($item->type() == 'textarea'): ?>

                <div class="form-element <?= $item->name()->value() ?>">
                    <label for="<?= $item->name()->value() ?>">
                    <?= $item->name()->value() ?> <abbr title="required">*</abbr>
                    </label>
                    <textarea id="<?= $item->name()->value() ?>" name="<?= $item->name()->value() ?>" required>
                        <?= $data[$item->name()->value()]?? '' ?>
                    </textarea>
                    <?= isset($alert[$item->name()->value()]) ? '<span class="alert error">' . html($alert[$item->name()->value()]) . '</span>' : '' ?>
                </div>

            <?php elseif($item->type() == 'selection'): ?>

            <div class="container form-selection <?= $item->name()->value() ?>">

                <p><?= $item->text() ?></p>
                
                <div class="container row">

                <?php foreach($item->option()->toStructure() as $option): ?>
                
                    <div class="form-element <?= $item->name()->value() ?>-<?= $option->name() ?>">
                        <label for="<?= $item->name()->value() ?>">
                            <?= $option->name() ?>
                        </label>

                        <input type="radio" id="<?= $option->name() ?>" name="<?= $item->name()->value() ?>" value="<?= $option->name() ?>" required>
                            <?= isset($alert[$item->name()]) ? '<span class="alert error">' . html($alert[$item->name()]) . '</span>' : '' ?>                        
                    </div>

                <?php endforeach ?>

                </div>

            </div>

            <?php else: ?>

                ! NO FIELD CONFIGURED !

            <?php endif ?>

        <?php endforeach ?>

        <input type="submit" name="submit" value="senden">

    </form>
    <?php endif ?>
<?php
$fieldnames = $page->form()->toStructure()->pluck('name', ',');
$data = [];

foreach ($fieldnames as $fieldname) {

   $data[trim($fieldname)] = get(trim($fieldname));

}
dump($data)