Tutorial on Form Validation & Emailing?

Thank you all for giving me hints and tips - it has been very helpful, indeed.
In order to help other newbies, I’ll document here what my current steps are for getting an ajax-based contact form going in Kirby. For someone new to web development getting this going can seem daunting, so hopefully this guide - of sorts - might help… The end result is that we can have a form processor without having to add extra template/snippet/controller/content pages - it’s all done with a route and a plugin.

The code here is built from several different sources, including a post from @Targoran, hints by @texnixe and @mzur, code from @bastianallgeier and guidance from @distantnative. Also, had to do quite a bit of research on GitHub and StackOverflow, to make sure the code was reasonably sound. So, here goes.

Follow these 4 steps:

Step 1

Place the following lines in your site’s ‘config.php’, to create a form-processing route:

c::set('routes', array(
  array(
    // in 'pattern', enter the same url being called from your ajax javascript function
    'pattern' => 'api/form',
    'method' => 'POST',
    'action' => function() {
      // check whether this is an ajax request, and respond with an error if it isn't
      if(!kirby()->request()->ajax()){ return response::error("Page Not Found!","404");}
      // process the form data, send the email - and get the result (as an array)
      $data = mailFormData(kirby()->request()->data());
      // respond with the result - in JSON format
      return response::json($data);
      }
    )
));

Step 2

In one of your templates or snippets, setup a contact form - such as the one below:

<form id="my-form">
    
    <!-- for displaying the results of the sending operation -->
    <span class="form-result"></span>
    
    <label for="my-form-name">Your Name*</label>
    <input type="text" name="name" id="my-form-name" required>
    
    <label for="my-form-email">Your Email*</label>
    <input type="email" name="email" id="my-form-email" required>
    
    <label for="my-form-phone">Your Phone</label>
    <input type="tel" name="phone" id="my-form-phone">
    
    <!-- a 'honeypot' field, to help us detect spam bots (use css to hide it) -->
    <input type="text" name="website" id="my-form-website" value="" class="hidden">
    
    <label for="my-form-message">Your Message</label>
    <textarea name="message" id="my-form-message" rows="9"></textarea>
    
    <button type="submit" name="submit">Send</button>
    
</form>

You can add or remove fields to your form, as you wish. You should also add css classes to its elements, and style the form according to your requirements.

Step 3

In your form template/snippet, right after your form element, place the following javascript:

<script>
    // AJAX FORM PROCESSING
    $('#my-form').on('submit', function(e){
        e.preventDefault();
        var form = $(this);
        $.ajax({
            type: 'POST',
            // use the same url here as the 'pattern' in your route
            url: 'api/form',
            data: form.serialize(),
            success: function(result){
                // form data successfully reached form processor api
                if(result.success){
                    // message successfully sent
                    form.find('.form-result').text('Your message was sent successfully - thank you!');
                } else {
                    // an issue was encountered
                    if(result.errors == undefined || result.errors == null || result.errors.length == 0){
                        // no validation errors - an email sending error was encountered
                        form.find('.form-result').text(result.msg);
                    } else {
                        // a validation error was encountered
                        var msg = "Please note: <br>";
                        if(result.errors.indexOf('name') != -1){ 
                            msg += "Name field must not be empty. <br>";
                         }
                        if(result.errors.indexOf('email') != -1){ 
                            msg += "Email field must contain a valid email. <br>";
                        }
                        if(result.errors.indexOf('website') != -1){ 
                            msg += "You seem to be a robot. <br>";
                        }
                        form.find('.form-result').html(msg);
                    }
                }
            },
            error: function(result){
                // the form was unable to reach processor api
                form.find('.form-result').text('Error '+ result.status + ' - unable to process form: ' + result.statusText);
            },
            dataType: 'json'
        });
    });
</script>

Note that the javascript above uses jQuery.

Step 4

In your ‘site/plugins’ folder, create another folder called ‘mail-form-data’. Inside that folder, create a new document, and call it ‘mail-form-data.php’. Inside this document, place the following PHP function, and customise it to suit!:

<?php

function mailFormData($data) {
    // $data (array) contains the form's sent data
    $name = $data['name'];
    $email = $data['email'];
    $phone = $data['phone'];
    $website = $data['website'];
    $message = $data['message'];
    
    // perform form data validation - we can use Kirby's validators,
    // available via "v":
    $errors = array();
    
    if(empty(trim($name))){ $errors[] = 'name'; }
    if(!v::email($data['email'])) { $errors[] = 'email'; }
    if(!empty($website)){ $errors[] = 'website'; }
    
    $result = array();
    $result['errors'] = $errors;
    
    // if we have validation errors, we can stop and return them:
    if(!empty($errors)){
        $result['success'] = false;
        $result['msg'] = 'Validation Failed';
        return $result;
    }
    
    // if we have no errors, we can go ahead and build an email message.
    // $to, $from and $subject can be hard-coded here, or can alternatively
    // be retrieved from values entered by the user in a page:
    $to = 'recipient@example.com';
    $from = $email;
    $subject = 'Message From Your Website';
    $body = <<<BODY

From: {$name}
--------------------------------------------------------
Email: {$email}
--------------------------------------------------------
Phone: {$phone}
--------------------------------------------------------
Message:

{$message}
BODY;
    
    // now, let's try sending the email:
    $email = email(array('to' => $to,'from' => $from,'subject' => $subject,'body' => $body));
    if($email->send()){
        // email was sent successfully
        $result['success'] = true;
        $result['msg'] = "Email sent successfully.";
    } else {
        // email delivery was not successful - report error
        $result['success'] = false;
        $result['msg'] = 'Email Delivery Failed: ' . $email->error()->message();
    }
    
    return $result;
}

Please be aware that it’s likely that this code still has errors, and can be optimised. Hopefully others will continue to tip in and improve on it!

13 Likes