Ajax CakePHP Contact Form

OK the past few posts have been dealing with Contact forms and howto create them with CakePHP along with file uploads. This post is going to be about howto create a contact form which works with Ajax using jQuery. Updates are coming thick and fast with the framework and this is using 2.3.0 and will be workable with the whole 2.x releases.

Database Setup

Like with previous examples I'm going to use a database to store submitted contact forms, the only difference is that this isn't going to be dealing with file uploads and I've added a few extra fields to validate.

CREATE DATABASE `cakephp_2_3`;

USE `cakephp_2_3`;

CREATE TABLE IF NOT EXISTS `contacts` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(255) DEFAULT NULL,  
  `email` VARCHAR(255) DEFAULT NULL,
  `telephone` VARCHAR(255) DEFAULT NULL,
  `message` TEXT DEFAULT NULL,
  `created` DATETIME DEFAULT NULL,
  `modified` DATETIME DEFAULT NULL
);

Rename and edit the database.php.default to database.php and enter your details:

public $default = array(
	'datasource' => 'Database/Mysql',
	'persistent' => false,
	'host' => 'localhost',
	'login' => 'cakephp230',
	'password' => 'cakephp230',
	'database' => 'cakephp_2_3',
	'prefix' => '',
	//'encoding' => 'utf8',
);

Controller Setup

The Controller is fairly standard with a contact form, the only difference is that we're using the RequestHandler component in order to get additional info about the request, specifically in this case we need to know about Ajax requests. What we've done is switch the layout to the ajax one if it's an ajax request, this gets rid of all the header & footer HTML and simply outputs the page content.

<?php
class ContactController extends AppController {

	public $components = array('RequestHandler');

	public function beforeFilter() {
		parent::beforeFilter();

		// Change layout for Ajax requests
		if ($this->request->is('ajax')) {
			$this->layout = 'ajax';
		}
	}

	public function index() {
		// form posted
		if ($this->request->is('post')) {
			// create
			$this->Contact->create();

			// attempt to save
			if ($this->Contact->save($this->request->data)) {
				$this->Session->setFlash('Your message has been submitted');
				$this->redirect(array('action' => 'index'));
			}
		}
	}
}

Contact View

Pretty much a standard form, similar to last time but with an added telephone field and also some added logic at the top. The script method is saying to include the contact.js script in the js folder and put it in the scriptBottom block. Blocks are very handy as they allow you add scripts in each of your view files and then in your layout file fetch them all at once.

<?php $this->Html->script('contact', array('block' => 'scriptBottom')); ?>
<h2>Contact</h2>
<?php 
echo $this->Form->create('Contact');
echo $this->Form->input('name');
echo $this->Form->input('email');
echo $this->Form->input('telephone');
echo $this->Form->input('message');
echo $this->Form->end('Submit');
?>

Layout Changes

In order to use Ajax we'll need a Javascript library, in this example it's jQuery so open up the app/View/Layouts/default.ctp layout file and add the following to the end of the file. Generally good practice to include scripts at the bottom of the page. As mentioned previously we're using View blocks and the fetch() method is going to output all the scripts we've added in our views. This is a simple example so it's just going to be the app/webroot/js/contact.js file.

	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
	<?php echo $this->fetch('scriptBottom'); ?>
	<?php echo $this->element('sql_dump'); ?>
</body>
</html>

With the way we're using Ajax we need a way of displaying Flash messages as CakePHP will automatically take care of redirects and just output the View HTML, so open up the app/View/Layouts/ajax.ctp and add the flash() line above the fetch('content') line.

<?php echo $this->Session->flash(); ?>
<?php echo $this->fetch('content'); ?>

Contact Model

The Model for the contact form is very simple and contains some rules for our fields, everything is required and there are a few extra rules for the email and the telephone to ensure people submit the right thing.

<?php
class Contact extends AppModel {

	public $name = 'Contact';

	public $validate = array(
		'name' => 'notEmpty',
		'email' => array(
			'rule' => 'email',
			'message' => 'Please enter a valid Email Address'
		),
		'telephone' => array(
			'rule' => 'numeric',
			'message' => 'Please enter a numeric Telephone Number'
		),
		'message' => 'notEmpty'
	);
}

Javascript

Ok so without Javascript everything should be working normally, you should be able to submit the form as long as it's validating. We're now going to enhance it and make it submit the form via Ajax (hopefully) without breaking anything!

Create the app/webroot/js/contact.js file mentioned earlier, we're going to hijack the submit logic of the form, post the form via Ajax using jQuery and update the page with the new HTML. This way any validation errors will be in the new HTML and will be displayed on screen. On a successfull form submission the form will clear and a nice flash message will display. Mine looks like:

$( document ).ready( function() {

	// Take over the submit action
	// attached to the body as the ajax call will replace the form 
	// and otherwise the onsubmit handler will be lost
	$('body').on('submit','#ContactIndexForm', function(e){

		// Update submit text to indicate something is happening
		$('.submit :input').val('Loading');

		// Post the form using the form's action and data
		$.post($(this).attr('action'), $(this).serialize())
		// called when post has finished
		.done(function(data) {
			// inject returned html into page
			$('#content').html(data);
		})
		// called on failure
		.fail(function(data) {
			// Flash message already on page
			if ($('#flashMessage').length) {
				// Fade out flash message
				$('#flashMessage').fadeOut('fast', function(){
					// Remove from page
					$(this).remove();
					// Inject another flash message
					$('h2').before('<div class="message" id="flashMessage">An Ajax error occured, please try again or refresh</div>');
				});
			} else {
				// Inject flash message
				$('h2').before('<div class="message" id="flashMessage">An Ajax error occured, please try again or refresh</div>');
			}
		});

	// return false to stop the page from posting normally
	return false;
	});

});

Wrapping Up

OK this one is quite short and sweet, quite a lot of ground covered if you're new to CakePHP & jQuery but hopefully quite self explanatory and useful if you're looking to build a contact form and improve it with Ajax. Unlike the last contact form this hasn't got any file uploads as they're difficult to implement along with Ajax submissions. If you need files uploading along with the form I would just use the bog standard way of doing things. The full source code for the Contact form is available via Github

Posted on 26th February 2013
4 years, 4 months, 3 weeks, 6 days ago

comments powered by Disqus