A CakePHP Contact Form

Share on facebook
Facebook
Share on twitter
Twitter
Share on reddit
Reddit
Share on linkedin
LinkedIn

Contact forms… simple, right?  In CakePHP, it’s not hard, but it requires a couple of techniques that seemed worth a post.  Because a contact form normally collects data that we’re not going to store in a database, a modelless form is a good choice.  And since we’ll be emailing the contact info, we have to configure our CakePHP app to send email.

I’ll be using the “CakeHRMS Tutorial” application for my contact form; the app code (with the addition of the contact form) is available via my GitHub repository.

Configuring CakePHP for Email

In this example, I configured my CakePHP app to use SMTP via my web host.  Chances are, for most production applications, you’d want something similar.

Edit config/app.php and find the “EmailTransport” section:

'EmailTransport' => [
    'default' => [
        'className' => 'Smtp',
        // The following keys are used in SMTP transports
       'host' => 'mail.my.domain',
       'port' => 26,
        'timeout' => 30,
        'username' => 'myaddress@my.domain',
        'password' => 'myPassword',
        'client' => null,
        'tls' => null,
    ],
],

You’ll need to look at your web host’s email configuration information in order to fill this in.  The “host” field is your web hosting account’s SMTP server address, the “port” as required for connecting to is, and the username and password are for an email address that you’ve configured through your web host.

The next block in config/app.php is “Email”:

'Email' => [
    'default' => [
        'transport' => 'default',
        'from' => 'mySenderAddress@my.domain',
        //'charset' => 'utf-8',
        //'headerCharset' => 'utf-8',
    ],
],

This sets the “from” address for “default,” the transport that we configured in the previous block.  You can configure multiples of both “EmailTransport” and “Email” which allows you the flexibility in your application to use different addresses, web hosts, etc.

Our app is ready to send emails; now we need to give the user a way to interface with it.

Adding a Modelless Form

A modelless form is a form that isn’t associated with a database table.  Most forms are used to manipulate data that we store in a database; that’s what we get out of the “bin/cake bake” process.  In this case, however, there’s no “contact” table, hence no model (i.e. no table or entity).

My contact form is very similar to the one found in the CakePHP documentation on modelless forms.  The code for the form lives in src/Form, a directory that doesn’t exist until you create it.  Let’s take a look at the code:

<?php
// in src/Form/ContactForm.phpnamespace AppForm;
use CakeFormForm;
use CakeFormSchema;
use CakeValidationValidator;
use CakeMailerEmail;

class ContactForm extends Form{
    protected function _buildSchema(Schema $schema)
    {
        return $schema->addField('name', 'string')
            ->addField('email', ['type' => 'string'])
            ->addField('body', ['type' => 'text']);
    }
    protected function _buildValidator(Validator $validator)
    {
        return $validator->add('name', 'length', [
                'rule' => ['minLength', 10],
                'message' => 'Please enter your name'
            ])->add('email', 'format', [
                'rule' => 'email',
                'message' => 'Please enter a valid email address',
            ])->add('body', 'length', [
                'rule' => ['minLength', 25],
                'message' => 'Please enter your message text',
            ]);
    }
    protected function _execute(array $data)
    {
        $email = new Email();
        $email->profile('default');
        $email->from([$data['email']])
            ->to('my.address@my.domain')
            ->subject('Web Site Contact Form')
            ->send([$data['body']]);
          return true;
    }
}

In function _buildSchema, we define the fields that we want to use for our form; we’re going to collect the sender’s name (“name”), his or her email address (“email”), and then the text of their message (“body”).

In _buildValidator we set validation rules for our fields and create messages to explain errors to our users.

Finally, _execute will send the email for us.  Our email object ($email) is an instance of the Email class, and we’re telling it to use the “default” profile that we set up in config/app.php.  We’ll send the email using the “from” address and body text submitted via the form.

The Controller

We’ll need a controller for our contact form.  Remember “convention over configuration” — since our form is “ContactForm,” our controller should be ContactController:

<?php
// In a controller
namespace AppController;

use AppControllerAppController;
use AppFormContactForm;

class ContactController extends AppController{
    public function index()
    {
        $contact = new ContactForm();
        if ($this->request->is('post')) {
            if ($contact->execute($this->request->data)) {
                $this->Flash->success('Your message has been sent; we'll get back to you soon!');
                $this->request->data['name'] = null;
                $this->request->data['email'] = null;
                $this->request->data['body'] = null;
            } else {
                $this->Flash->error('There was a problem submitting your form.');
            }
        }
        $this->set('contact', $contact);
    }
}

We’ve created an index method that creates a contact object ($contact) from the ContactForm class, and if our request is a POST we call the execute method of $contact with the data from the HTTP request.  This sends our email so we flash a “success” message and then clear out the form fields (as we’re not redirecting to another screen).

We still need one other piece: the template for the form.

The Template

In src/Template, create a “Contact” folder and create an index.ctp like this:

    <nav class="large-3 medium-4 columns" id="actions-sidebar">
        ...(your sidebar navigation goes here)...
    </nav>
</div>
<div class="employees view large-9 medium-8 columns content">
    <?= $this->Form->create($contact); ?>
    <?= $this->Form->input('name'); ?>
    <?= $this->Form->input('email'); ?>
    <?= $this->Form->input('body'); ?>
    <?= $this->Form->button('Submit'); ?>
    <?= $this->Form->end(); ?>
</div>

Nothing unusual here; this is pretty much just a normal form template.  When we invoke create($contact) we’re referencing the variable that we “set” in our controller.  The template’s “Submit” button invokes the index method of the ContactController, just like we’d expect.  Mostly, the big difference in “modelless” is that you don’t have a model or entity, so you have to put that code elsewhere; that’s the role of the code in src/Form/ContactForm.php.

If all went well – and assuming you have an email server upon which to test – you should now have a fully-operational “Contact” form.

More to explore

Photo by Caspar Camille Rubin on Unsplash

Getting WP Post and Postmeta in Single Rows

WordPress uses wp_posts to store post, page, and Custom Post Type (CPT) data the wp_postmeta table for Custom Metabox data. To retrieve this data you have to read for the post plus multiple linked postmeta rows. In this post, we conquer postmeta with subqueries.

Using Redis with WordPress on Ubuntu

Redis is “an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.” Translation? It makes websites run faster.

Nasa on Unsplash

Building an API Endpoint with Amp (PHP)

API endpoints in web apps are pretty typical these days, but there may be reasons to provide data outside of the context of an application. In this post, we’ll explore how to make a stand-alone API endpoint using PHP and Amp.

One Comment

Leave a Comment

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