User generated forms in EE
Recently, I released a new ExpressionEngine add-on: Low Freeform Field. This simple field type generates a drop down list with available Freeform fields and will display the selected field name in your templates. Now, this isn’t a big deal by itself, but combined with Matrix, it can allow you to create forms with channel entries. Let me show you how.
Freeform and Matrix
First of all, you need to have Freeform installed, a free from managing module by the add-on veterans at Solspace. In Freeform, you can define different form fields that can be populated by any number of forms. The only down side is, you still have to create the HTML for those forms in your templates and there’s no good way to automate this. Until now.
Secondly, you’ll need Pixel & Tonic’s Matrix. This field type allows you to create tabular data in channel entries, which can be used in a plethora of ways. You define the columns, and the client can add as many rows to the matrix as desired. Pixel & Tonic is also responsible for many other great field types, like P&T List which we’ll use in this example, too.
Channel and Fields
Now, to start automating from creation, we’ll create a designated channel called Forms. To this channel, we’ll add a couple of custom fields, for example:
form_description: a textarea for adding a form description;form_fields: a matrix field with all the form fields for this form;form_feedback: a textarea for the message displayed to the user after submitting the form;form_notify: a text input field where you can enter email addresses that will receive form notifications.
The fun starts with the Matrix field. To that, we’ll add the following columns:
field_name: the Low Freeform Field;field_label: the label associated with the form field;field_type: a drop down to determine the type of input field, eg. text, textarea, select, radio, checkbox…field_options: if the type is select, radio or checkbox, you can use this field to define the different options. P&T List is really useful here;field_required: a checkbox to select whether this field is required or not.
You could add more columns if needed. For example, you could add a field_class for custom styling or field_notes if you need to display per-field instructions. Below is a screenshot of the publish form with the above settings, but as you can imagine, the possibilities are endless.
The template
To bring it all together, we need to create a clever template. You could get creative and use an embed and relationships to add forms to other entries, but in this case, we’ll add the code to the index template in the forms template group. A link to a single form would look like this: domain.com/forms/my-custom-form. Here’s some code to go with it:
{exp:channel:entries channel="forms" limit="1"}
<h1>{title}</h1>
{if segment_3 == 'thanks'}
{form_feedback}
{/if}
{if segment_3 != 'thanks'}
{form_description}
{exp:freeform:form
collection="{title}"
notify="{form_notify}"
required="{form_fields search:field_required="y" backspace="1"}{field_name}|{/form_fields}"
return="{segment_1}/{segment_2}/thanks"}
<fieldset>
{form_fields}
<label for="{field_name}">
{field_label}
{if field_required == 'y'} <span class="required">*</span>{/if}
</label>
{if field_type == 'text'}
<input type="text" name="{field_name}" id="{field_name}" />
{if:elseif field_type == 'textarea'}
<textarea name="{field_name}" id="{field_name}" rows="10" cols="40"></textarea>
{if:elseif field_type == 'select'}
<select name="{field_name}" id="{field_name}">
{field_options}
<option value="{item}">{item}</option>
{/field_options}
</select>
{if:elseif field_type == 'radio'}
{field_options}
<label><input type="radio" name="{field_name}" value="{item}" /> {item}</label>
{/field_options}
{if:elseif field_type == 'checkbox'}
{field_options}
<label><input type="checkbox" name="{field_name}[]" value="{item}" /> {item}</label>
{/field_options}
{/if}
{/form_fields}
<button type="submit">Submit form</button>
</fieldset>
{/exp:freeform:form}
{/if}
{/exp:channel:entries}
After opening the channel:entries tag, we’ll open the freeform:form tag, using the entry’s title as the form collection. Using Matrix’ search option, we’ll define freeform’s required parameter. We can set more parameters, depending on the channel fields available.
Then we’ll loop through the form_fields. Use an advanced conditional to account for all the different input types and loop through the field_options if necessary. As you can see, the output is fully customisable.
Awesomeness achieved
This approach allows for fairly flexible custom forms, and with a bit of creative thinking, you could make it even better.
[ETA] Great minds think alike, so it seems. Here’s a write-up for the same idea in EE1 that I wasn’t aware of.
Comments
Thanks for the writeup. It is nice to see how this new resource can be utilized for client control (and fewer phone calls).
Tristan Blease also wrote up a similar how-to recently on his blog: http://tristanblease.com/blog/an-easier-way-to-build-forms-in-ee2 . However, his method is a little more complex, and might be overkill for some projects, where this method will work beautifully. Thanks again!
This is awesome Low; exactly what I was after. Thanks for the write up.
This is absolutely fantastic, Low - a real time saver for sure! Do you envision a way down the line of also dynamically implementing validation on the fields (other than just EE’s required” validation that is)? Using Ajax perhaps? I’m sure that complicates matters since validation is so dependent on field types used in the form itself. Just curious if you’ve got a basic means of implementing that using your field type as well.
Hey Low,
It’s a great piece of script, however, how would you go about file-uploads? i can’s seem to get them as a freeform fieldtype, since “It’s a pseudo field and not actually an existant field in Freeform” http://www.solspace.com/forums/viewthread/3967/#30312
@Jelle, yeah, that would be tricky.
I’d probably add a value ‘upload’ to the field_type drop down, and then use the conditional
{if field_type == 'upload'} file upload field here {/if}.Then I’d use PHP on output to determine the name=”" attribute for that upload field, since that needs a number appended to it:
file1,file2, etc. The PHP would look something like this:<input type="file" name="file<?=++$i?>" />Just be sure to add
<?php $i = 0; ?>somewhere above the advanced conditional block.