Tools Plugin – Part 2: Contact Form

I want to show how easy it is to make a solid and universal contact form in Cake(2).
The full code can be found in the github rep of the Tools plugin.

Model

The most important part first: We need a solid validation for the form. How many forms are out there that do not have any validation. The first thing I always do on about any public contact form: Hit the submit button and smile it if tells me that my empty form "has just been successfully sent". We can do better than that 🙂

class ContactForm extends ToolsAppModel {

    protected $_schema = array(
        'name' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '30'),
        'email' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '60'),
        'subject' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '60'),
        'message' => array('type' => 'text' , 'null' => false, 'default' => ''),
    );
    
    public $useTable = false;
    
    public $validate = array(
        'name' => array(
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'valErrMandatoryField',
                'last' => true
            )
        ),
        ...
    );

The _schema var mocks a database table so that we don’t really need one. This helps the FormHelper to generate the inputs (maxlength, type, …).
The validation rules will make sure the email is valid and the user actually entered some text.

Controller

The logic takes care of the validation on POST.
/Controller/ContactController.php

<?php
class ContactController extends AppController {

    public $uses = array('Tools.ContactForm');

    public function index() {
        if ($this->request->is('post') || $this->request->is('put')) {
            if (!$this->Session->check('Auth.User.id')) {
                $this->ContactForm->Behaviors->attach('Tools.Captcha');
            }
            $this->ContactForm->set($this->request->data);
            if ($this->ContactForm->validates()) {
                $name = $this->request->data['ContactForm']['name'];
                $email = $this->request->data['ContactForm']['email'];
                $message = $this->request->data['ContactForm']['message'];
    
                // send email with CakeEmail
            } else {
                // error flash message
            }

        }
        $this->helpers = array_merge($this->helpers, array('Tools.Captcha'));
    }
}

As you can see it only sends emails after successfully validating.
For public contact forms I usually like some easy captcha behavior attached so that spam doesnt reach me. You can omit that, of course.
Also note: you should use your own setFlash() method instead of mine!

Last but not least: View

/View/Contact/index.ctp

<?php echo $this->Form->create('ContactForm');?>
    <fieldset>
        <legend><?php echo __('contactLegend');?></legend>
    <?php
        echo $this->Form->input('name');
        echo $this->Form->input('email');

        echo $this->Form->input('subject');
        echo $this->Form->input('message', array('rows'=>15));

        if (!$this->Session->read('Auth.User.id')) {
            echo $this->Captcha->input('ContactForm');
        }
    ?>
    </fieldset>
<?php echo $this->Form->submit(__('Submit')); ?>
<?php echo $this->Form->end();?>

Result

Browse to /contact/
That should display the form right away.
After the successful POST you should redirect back to the contact form (emptied then) or to another custom page.

Final notes

Currently, the Model is in the Tools.Plugin. You could put it into your normal app model folder, as well.
But I use it in many projects and therefore I want to keep it dry. Feel free to adjust any of the code to your own needs. Same goes for my own custom methods like $this->Common->flashMessage() etc.

Some might wonder why "ContactController" and not "ContactsController". The latter would be cake conventions. But there are situations where you can and should diverge form those. As in this case where "contacts" would mean more like sth to manage your addressbook. It also makes the url more meaningful out of the box (without any custom routes). With cake2 this all works without any additional customization.

The other thing: I didn’t name the Model "Contact" but "ContactForm" in order to not create possible conflicts which exactly such contacts/contact mangement MVCs (as I did in a contact management suite).

4.75 avg. rating (93% score) - 4 votes

11 Comments

  1. Thankyou for this tutorial, how do I go about styling the Captcha labels? It’s coming though with captchaExplained and then captchaTip after the field.

    Thanks

  2. Hi

    Wen a form is sent and its content is not valid, when you re display the form, fields are not filled with the content use when the form was submited.

    Is there a reason for this ?

  3. Jep, then your code is wrong.
    Because – doing it the right way – the form will keep the information you posted.
    Without more information I cannot tell you, though, what exactly you are doing wrong there.

  4. Hi Mark,

    When I click submit and nothing happen.

    I guess your tools is not compatible with Cakephp 2.4.1

    Or I did something wrong?

    I just followed your instruction step by step.

    Jian Zhan

  5. a) its /contact (lowercase c)
    b) its compatible with all 2.4 and 2.5 versions. you seem to miss the HTML5 validation going on there. disable it if unneeded.

  6. ONLY http://polarlamp.com/Contact does work.
    What’s wrong with my setup?

    http://polarlamp.com/contact does not work.

    How to disable HTML5 validation?

    Error: Contact.ContactController could not be found.
    Error: Create the class ContactController below in file: /home/content/95/4770795/html/PolarLamp/cakephp/plugins/contact/Controller/ContactController.php
    &lt;?php
    class ContactController extends ContactAppController {

    }
    Notice: If you want to customize this error message, create PolarLamp/View/Errors/missing_controller.ctp

  7. Ho do I use your Captcha in my controller?

    I checked your previous post spmewhere:
    $this-&gt;Model-&gt;Behaviors-&gt;attach(‘Captcha’);
    or alternately for all actions in one controller via model:
    var $actsAs = array(‘Captcha’);

    Sincerely

    Jian Zhan

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.