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!