Skip to content

Contact form integration

Lukas Bestle edited this page Jul 30, 2023 · 5 revisions

This tutorial explains how you can pass the product or plan configuration to a contact form when the "request product"/"request plan" button is clicked.

For this tutorial we will use the Uniform plugin by Martin Zurowietz. If you already use a different plugin or prefer to use Kirby's core methods, you can adapt the code accordingly. If you want to follow the example, please first install the Uniform plugin as explained in its docs.

The example code from this tutorial treats the configuration data as optional, so you can use the same page as a general contact form.

1. Process the configuration data and form submission in the controller

First we will create a page controller for the form template. In this example we will call it site/controllers/form.php.

The controller does all the heavy lifting. This includes setting up Uniform, getting the configuration data from the form and handling the form submission:

<?php

use Uniform\Form;

return function ($kirby) {
    $form = new Form([
        'email' => [
            'rules' => ['required', 'email'],
            'message' => 'Please enter a valid email address',
        ],
        'message' => [
            'rules' => ['required'],
            'message' => 'Please enter a message',
        ],
        'roomle-configuration' => []
    ]);

    // get the configuration data from the request or
    // fall back to the unescaped data from Uniform
    $configurationData = $kirby->request()->get(
        'roomle-configuration',
        htmlspecialchars_decode($form->old('roomle-configuration'))
    );

    // turn the raw configuration string into a nice object structure;
    // the plugin automatically handles both plan and product configurations
    $plan = $configurationData ? roomlePlan($configurationData) : null;

    // only submit the form if the `submit` value is set;
    // just listening for POST requests won't work because the
    // configuration data is also transmitted via a POST request
    if ($kirby->request()->get('submit')) {
        $form->emailAction([
            'to'   => 'me@example.com',
            'from' => 'info@example.com',

            // we'll use a custom plain text email template (see step 4)
            'escapeHtml' => false,
            'template'   => 'roomle-contact'
        ]);
    }

    return compact('form', 'plan');
};

2. Display the form and configuration in the template

The controller passes two variables to our form template, $plan with all the Roomle configuration data and $form from Uniform.

We can now use these variables in site/templates/form.php to display both the contact form and the product configuration:

<?php if ($plan && $url = $plan->configuratorUrl()): ?>
<a href="<?= esc($url, 'attr') ?>">Back to the configurator</a>
<?php endif ?>

<form action="<?= $page->url() ?>" method="POST">
    <label for="email">Email address</label>
    <input id="email" name="email" type="email" value="<?= $form->old('email') ?>">

    <label for="email">Message</label>
    <textarea id="message" name="message"><?= $form->old('message') ?></textarea>

    <?= csrf_field() ?>
    <?= honeypot_field() ?>

    <?php if ($plan): ?>
    <input name="roomle-configuration" type="hidden" value="<?= esc($plan->toJson(), 'attr') ?>">
    <?php endif ?>

    <input type="submit" name="submit" value="Submit">
</form>

<?php if ($form->success()): ?>
Success!
<?php else: ?>
<?php snippet('uniform/errors', ['form' => $form]) ?>
<?php endif ?>

<?php if ($plan): ?>
<h2>Configuration data</h2>

<?php if ($plan->hasId() === true): ?>
<?= $plan->thumbnail()->html(['class' => 'configuration-image']) ?>
<?php endif ?>

<?php foreach ($plan->groupedItems() as $item): ?>
<h3><?= esc($item->count()) ?>x <?= esc($item->label()) ?></h3>
<p><?= esc($item->size()) ?></p>

<?= $item->perspectiveImage()->html(['class' => 'configuration-image']) ?>

<table>
    <tr>
        <th>Count</th>
        <th>Article number</th>
        <th>Name</th>
    </tr>

    <?php foreach ($item->parts() as $part): ?>
    <tr>
        <td><?= esc($part->count()) ?></td>
        <td><?= esc($part->articleNr()) ?></td>
        <td>
            <?= esc($part->label()) ?>
            <dl class="parameters">
                <?php foreach ($part->parameters() as $parameter): ?>
                <dt><?= $parameter->label() ?>:</dt>
                <dd><?= $parameter->valueLabel() ?></dd>
                <?php endforeach ?>
            </dl>
        </td>
    </tr>
    <?php endforeach ?>
</table>
<?php endforeach ?>
<?php endif ?>

<style>
.uniform__potty {
  position: absolute;
  left: -9999px;
}
</style>

3. Email template

In the resulting email, Uniform will by default print the raw data for each form field. This would mean that the Roomle configuration data is printed as a JSON string.

For a more user-friendly output, we need a custom email template in site/templates/emails/roomle-contact.php:

<?= $message . "\n" ?>

<?php if ($configuration = $data['roomle-configuration']): ?>
-------------------
Configuration data:

<?= roomlePlan($configuration) ?>
<?php endif ?>

This email template is already referenced in the controller from step 1.

Warning: In this example we send a plain text email. It is strongly recommended to keep the setup like this. If you include a HTML email template, security attacks like cross-site scripting (XSS) are possible. This is because the configuration data is received from the client and can be manipulated by attackers. If you really need a HTML template, please ensure that you escape the output properly in custom snippets (see below).

4. Point the plugin to the contact form

Now you need to tell the Roomle plugin that it should submit the configuration data to your contact form whenever the "request product"/"request plan" button is clicked.

You can either do this by selecting the contact form page in the block settings or by setting a global default in site/config/config.php:

<?php

return [
  'lukasbestle.roomle' => [
    'target' => 'contact'
  ]
];

Customization

More fields in the page template

The $plan object and its children also have more fields and methods you can use for output on the form page (e.g. the part price). To see what is possible, please take a look at the plugin classes, particularly Configuration, Parameter, Part and Plan.

Custom configuration rendering in the email

The email template from step 3 prints the whole $plan object. Internally, this will render four snippets: roomle/plan.php, roomle/configuration.php, roomle/part.php and roomle/parameter.php.

If you want to customize the rendering in the email, you have two options:

  • You can copy the default snippets from site/plugins/roomle/src/config/snippets/roomle to site/snippets/roomle and customize them.
  • You can modify the email template from step 3 directly (similar to the form page template from step 2).

With either of these solutions, you can also generate an HTML email as you have direct access to the individual fields of the configuration and can escape their values for HTML output.