Ted Avery Ted Avery - 1 year ago 58
PHP Question

How to use ExpressionEngine form validation class in module to repopulate form in template?

Is it possible to display errors and repopulate fields on a form that is hard coded into a template? So far I’ve only figured out how to display the errors in a module’s view, but not a template.

Based on the logic of ExpressionEngine, I’m guessing I need to somehow make the validation errors visible through a tag in my module, or even generate the whole form from the module, but I’m not sure how to best approach this.

Here is what I have right now.

function submit_form()

$this->EE->form_validation->set_rules('first_name', 'First Name', 'required');
$this->EE->form_validation->set_rules('last_name', 'Last Name', 'required');
$this->EE->form_validation->set_rules('address', 'Address', 'required');
$this->EE->form_validation->set_rules('city', 'City', 'required');
$this->EE->form_validation->set_rules('province', 'Province', 'required');

if ($this->EE->form_validation->run() == FALSE)
return $this->EE->load->view('form_errors');
// success

And for testing, the view simply contains:

echo validation_errors();

Can anyone help?

Answer Source

Great question, and one which took me a long time to figure out the best way to solve.

The CodeIgniter Form Validation library is great, but can only be used with proper views and controllers, so it doesn't work out of the box when you are developing a front end tag.

Normally, the preferred way of submitting a front-end form is to register an 'Action' in your upd.addon.php file (I'm guessing you have done this for your submit_form() function). This is then assigned a number, which you can post with the url /index.php?ACT=37 or something similar. This is a good system, because it means we know the form submission came from our module. However, with input forms, this is a hindrance, because it means we can't repopulate input fields. You therefore need to configure the input form to post back to the current URL, and wait until the template engine tries to render your tag before handling the form submission.

The easiest, and visually ugliest way to achieve this, is to use $this->EE->output->show_user_error(FALSE, array_of_errors). You can actually use this from either an action, or within your module code. It displays the standard grey EE message page we have all grown to know and hate.

With that sort of intro, you probably knew the solution wasn't going to be quite that simple, right? Here's a skeleton of a tag function which implements inline error checking:

function my_form()
    // load default tag variables
    $tag_vars = array();
    $tag_vars[0] = array(
        'first_name' => '',
        'error:first_name' => '',
        'last_name' => '',
        'error:last_name' => ''

    // handle a form submission
    if ($this->EE->input->post('my_form_hidden') == '1'))
        // load POST data into tag
        $tag_vars[0]['first_name'] = $this->EE->input->post('first_name', TRUE);
        $tag_vars[0]['last_name'] = $this->EE->input->post('last_name', TRUE);

        // use CI validation library to check submission
        $this->EE->form_validation->set_rules('first_name', 'lang:first_name', 'required');
        $this->EE->form_validation->set_rules('last_name', 'lang:first_name', 'required');

        $valid_form = $this->EE->form_validation->run();
        if ($valid_form)
            // probably save something to database, then redirect
            $form_errors = array();
            foreach (array('first_name', 'last_name') as $field_name)
                $field_error = form_error($field_name);
                if ($field_error)
                    $form_errors[] = $field_error;
                    $tag_vars[0]['error:'.$field_name] = $field_error;

            if ($this->EE->TMPL->fetch_param('error_handling') != 'inline')
                // show default EE error page
                return $this->EE->output->show_user_error(FALSE, $form_errors);

    // parse and output tagdata
    $out = $this->EE->functions->form_declaration(array(
        'action' => $this->EE->functions->fetch_current_uri(),
        'hidden_fields' => array('my_form_hidden')));
    $out .= $this->EE->TMPL->parse_variables($tagdata, $tag_vars);
    return $out.'</form>';

This way, the designer can specify error_handling="inline" in the tag if they want inline errors, otherwise they will just be redirected to the standard error form. If they do request inline error handling, they will simply need to make sure their inputs look like this:

<input type="text" name="first_name" value="{first_name}" />

Note the hidden field we submit along with the form, this allows us to ensure we only handle submission of this form, and not any other forms on the page, such as a login form or something. If you have more than one instance of your form on a page (say inside a channel entries loop or something), you will need to implement some trickery to make sure that you only handle the form instance which was submitted, for example by submitting along entry_id as a hidden field too.

Glad to get that all documented, hopefully this will be useful to other EE developers too!