Nov 19, 2009

Freeform + FieldFrame = ExpressionEngine Form Builder

A form builder is a regular request for some clients when they are looking for a content management system. There aren’t that many CMS that have this kind of functionality built-in, and even if they do, the implementation is usually less than desirable.

If you follow along with what I’ve been up to, you can probably tell that I have been on a big ExpressionEngine kick. I’ve been using it on a ton of projects recently, and I love it for its flexibility. Although it does lack a true form builder, I figured out a simple way to build one.

If you are impatient and just want to get to the demo, go ahead. Although the form will look like nothing special, as I have just added some simple styling. The real magic of the form builder happens in the Control Panel.

We Need Add-ons

This form builder would not be possible without the use of some awesome add-ons:

Creating a Field Group

I built a custom Field Group called Forms, which consisted of four fields:

The first three are pretty simple, so I will just give a quick run through on the specifications for those fields and give a more detail review of the Fields field:

Template

This is where you select the Freeform template that will be used for the email. This field is not completely necessary, as just using the default template should be good enough, but I figured since it was an option, to throw it in.

Notification Email Addresses

This is where you will enter the email address(es) that you want to email when the form is submitted. Since Freeform wants multiple email addresses separated by the | character, I added some help text describing that as well.

Return Page

This is where you enter the URL to the thank you page. Once a user successfully submits the form, they will be redirected here.

Fields

This is where the magic happens. This field is a FF Matrix field type that consists of five other fields. Each row of the FF Matrix represents one field (or field group) in the form. I’ll give a brief description of each field of the FF Matrix and show you a screenshot so you can get a better picture:

Label

This is where you will enter the label for the form field:

Field

This field pulls in all of the fields that have been entered into the Freeform module. So you are presented with a dropdown of all of the fields, and you just select the appropriate one:

Field Type

This is where you select the type of form field you want to use. I put in a few different options, but this could be expanded upon too:

Is this field required?

With most forms, there are some fields that are required and some that are not. If you want the field to be required, you just check the checkbox and the Freeform module will make sure it is filled in:

Options

Finally, this field is used when you select a field type that has multiple options. For example, if you select a “Select” field type, you need to provide the options to populate it. So you enter the options separated by the | character:

FieldFrame Matrix Configuration

So that is all you need for the Field Group, now go ahead and create a Forms Channel (or Weblog) and make sure to assign the Forms Field Group to it.

Now, when you go to publish a Forms entry, you should see something like this:

New Forms Entry

I made a demo form showing off the various types of form fields. Here is what that entry looks like in the Control Panel:

Demo Form Entry in Control Panel

Template

Now that we have our Channel and Field Group all setup, we need to deal with the template. You could really setup your template/template group in various ways, so I’m not going to get into those details. One detail that you need to know is that I enabled PHP for my template.

First, we just need to start with a simple exp:weblog:entries tag:

{exp:weblog:entries weblog="forms" limit="1" disable="categories|category_fields|member_data|pagination|trackbacks"}

{/exp:weblog:entries} 

Next, we want to start the Freeform form tag:

{exp:weblog:entries weblog="forms" limit="1" disable="categories|category_fields|member_data|pagination|trackbacks"}
 {exp:freeform:form form_name="{url_title}" form_id="{url_title}" template="{form_template}" notify="{form_notify}" return="{form_return}"}
 …
 {/exp:freeform:form}
{/exp:weblog:entries} 

Since we are inside of the exp:weblog:entries tag, we have access to everything that is being returned from that entry. If you have any questions about the various parameters, refer to the Freeform Documentation.

So far, the values that we are passing into the parameters are pretty straightforward, just custom fields from our entry. But, passing in the required parameter is a little more challenging, since we have to loop through all of the rows in our FF Matrix.

If we refer to the FF Matrix documentation, we can see how to loop through the rows in the matrix. I only want to return rows that have the required checkbox checked, and I want to separate the names of the fields with the | character. Then, I use the backspace parameter to remove the last one:

{exp:freeform:form form_name="{url_title}" form_id="{url_title}" template="{form_template}" notify="{form_notify}" return="{form_return}" <strong>required="{form_field search:required='y' backspace='1'}{field}|{/form_field}"</strong>}

{/exp:freeform:form} 

Next, we want to add a title, ordered list that will contain the form fields, and the submit button:

{exp:freeform:form form_name="{url_title}" form_id="{url_title}" template="{form_template}" notify="{form_notify}" return="{form_return}" required="{form_field search:required='y' backspace='1'}{field}|{/form_field}"}
 <h1>{title}</h1>
 <ol class="forms">
  …
  <li class="buttons"><input type="submit" name="submit" value="submit" /></li>
 </ol>
{/exp:freeform:form} 

Now here is where the code gets interesting. Just like we did with the required parameter, we want to loop through each row in the FF Matrix and display a list item for each one:

{form_field}
 <li>
 …
 </li>
{/form_field} 

First, let’s display the label, if there is one, and denote if the field is required or not:

{if "{label}"}
 <label for="{field}">{if "{required}" == "y"}<em class="required">*</em>{/if}{label}</label>
{/if} 

The rest of the code is a big conditional statement which depends upon the Field Types that you specified in the FF Matrix. Let’s first take a look at the Text Input, Textarea, and Checkbox types since they are pretty straightforward:

{if "{type}" == "Text Input"}
 <input type="text" name="{field}" id="{field}" />
{if:elseif "{type}" == "Textarea"}
 <textarea name="{field}" id="{field}"></textarea>
{if:elseif "{type}" == "Checkbox"}
 <input type="checkbox" class="checkbox" name="{field}" id="{field}" />

{/if} 

So we are just checking the type column in the FF Matrix, and then displaying the rest of the data from the row in the appropriate form field.

Next, let’s walk through what happens if you select the Select or Multi-select types:

{if:elseif "{type}" == "Select" || "{type}" == "Multi-select" && "{options}"}
 <select name="{field}{if "{type}" == "Multi-select"}[]{/if}" id="{field}"{if "{type}" == "Multi-select"} multiple="multiple" size="4"{/if}>
  <?php $items = explode("|", '{options}'); ?>
  <?php foreach ($items as $item) { ?>
   <option value="<?php echo $item; ?>"><?php echo $item; ?></option>
  <?php } ?>
 </select>

{/if} 

So first, we are displaying the select element, and if it is a Multi-select, we are adding [] to the name attribute, and adding the multiple and size attributes.

Next, we want to take the string of options that are separated by the | character, and create an array out of the string using the explode function.

Then, we loop through the array, and display the options for each one.

Finally, we do virtually the same thing for the Radio Group and Checkbox Group field types:

{if:elseif "{type}" == "Radio Group" && "{options}"}
 <ul class="group">
  <?php $items = explode("|", '{options}'); $count = 1; ?>
  <?php foreach ($items as $item) { ?>
   <li><input type="radio" class="radio" name="{field}" id="{field}<?php echo $count; ?>" value="<?php echo $item; ?>" />
   <label for="{field}<?php echo $count; ?>"><?php echo $item; ?></label></li>
   <?php $count++; ?>
  <?php } ?>
 </ul>
{if:elseif "{type}" == "Checkbox Group" && "{options}"}
 <ul class="group">
  <?php $items = explode("|", '{options}'); $count = 1; ?>
  <?php foreach ($items as $item) { ?>
   <li><input type="checkbox" class="checkbox" name="{field}[]" id="{field}<?php echo $count; ?>" value="<?php echo $item; ?>" />
   <label for="{field}<?php echo $count; ?>"><?php echo $item; ?></label></li>
   <?php $count++; ?>
  <?php } ?>
 </ul>
{/if} 

So that’s it really. Here is the full template:

{exp:weblog:entries weblog="forms" limit="1" disable="categories|category_fields|member_data|pagination|trackbacks"}

 {exp:freeform:form form_name="{url_title}" form_id="{url_title}" template="{form_template}" notify="{form_notify}" return="{form_return}" required="{form_field search:required='y' backspace='1'}{field}|{/form_field}"}

  <h1>{title}</h1>
  <ol class="forms">
   {form_field}
    <li>
     {if "{label}"}
      <label for="{field}">{if "{required}" == "y"}<em class="required">*</em>{/if}{label}</label>
     {/if}
     {if "{type}" == "Text Input"}
      <input type="text" name="{field}" id="{field}" />
     {if:elseif "{type}" == "Textarea"}
      <textarea name="{field}" id="{field}"></textarea>
     {if:elseif "{type}" == "Checkbox"}
      <input type="checkbox" class="checkbox" name="{field}" id="{field}" />
     {if:elseif "{type}" == "Select" || "{type}" == "Multi-select" && "{options}"}
      <select name="{field}{if "{type}" == "Multi-select"}[]{/if}" id="{field}"{if "{type}" == "Multi-select"} multiple="multiple" size="4"{/if}>
       <?php $items = explode("|", '{options}'); ?>
       <?php foreach ($items as $item) { ?>
        <option value="<?php echo $item; ?>"><?php echo $item; ?></option>
       <?php } ?>
      </select>
     {if:elseif "{type}" == "Radio Group" && "{options}"}
      <ul class="group">
       <?php $items = explode("|", '{options}'); $count = 1; ?>
       <?php foreach ($items as $item) { ?>
        <li><input type="radio" class="radio" name="{field}" id="{field}<?php echo $count; ?>" value="<?php echo $item; ?>" />
        <label for="{field}<?php echo $count; ?>"><?php echo $item; ?></label></li>
        <?php $count++; ?>
       <?php } ?>
      </ul>
     {if:elseif "{type}" == "Checkbox Group" && "{options}"}
      <ul class="group">
       <?php $items = explode("|", '{options}'); $count = 1; ?>
       <?php foreach ($items as $item) { ?>
        <li><input type="checkbox" class="checkbox" name="{field}[]" id="{field}<?php echo $count; ?>" value="<?php echo $item; ?>" />
        <label for="{field}<?php echo $count; ?>"><?php echo $item; ?></label></li>
        <?php $count++; ?>
       <?php } ?>
      </ul>
     {/if}
    </li>
   {/form_field}
   <li class="buttons"><input type="submit" name="submit" value="submit" /></li>
  </ol>
 {/exp:freeform:form}  
{/exp:weblog:entries} 

Conclusion

This is not meant to be an all-inclusive form builder, as you can see there are many other parameters for the Freeform module. But, this was meant to show a creative use of ExpressionEngine and EE add-ons to add functionality that is not directly built into the CMS.

So go forth and create.