Welcome Guest, Not a member yet? Register   Sign In
[library concept] Harmony (JavaScript and Server side validation simplified)
#1

[eluser]litzinger[/eluser]
I have no idea if this has been attempted and if I'm trying to reinvent the wheel here, but last night I had an idea to try and combine my favorite JS form validation library (jQuery and http://jquery.bassistance.de/validate/demo-test/) with CI. Usually I have to type in my rules twice, and maintain them in two locations for each form. Below is my first attempt at combining the two. The idea is to put all the form values into a config array, and use that array to populate the CI form validation fields and rules, and to print out the rules in the HTML form fields to be used by the jQuery validation. Below is a simple concept, and I'm stuck on setting the CI messages (via set_message()) to display the custom message when JS is disabled.

Let me know if this is a waste of time, or if I'm onto something.

site_settings.php
Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

$config['harmony_forms']['test_form'] = array(
        'name' => array(
                'type' => 'text',
                'rules' => 'required',
                'label' => 'First Name',
                'error_message' => 'Please enter your first name'
            ),
        'email' => array(
                'type' => 'text',
                'rules' => 'required|valid_email',
                'label' => 'Email Address',
                'error_message' => 'Please enter a valid email address'
            )    
    );

?>

Harmony.php
Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

class Harmony {
    
    function Harmony()
    {
        $CI =& get_instance();
        $this->forms = $CI->config->item('harmony_forms');
    }
    
    function pre_form($form_class, $current_form, $error_class = false)
    {
        $CI =& get_instance();
        $this->current_form = $current_form;
        $error_class = ($error_class) ? $error_class : 'error';
                
        $print = '
                    $(function(){  
                        $("form.' . $form_class . '").validate({
                            focusInvalid: true,
                            errorLabelContainer: $("ol.' . $error_class . '"),
                            wrapper: "li"
                        });
                    })
                ';
        $print .= '<ol class="' . $error_class . '">' . $CI->validation->error_string . '</ol>';
        
        return $print;
    }
    
    function input($field_name, $field_value, $field_attributes)
    {
        $CI =& get_instance();
        $print = NULL;
        $type = $this->_get_attr_val($field_name, 'type');
        $title = $this->_get_attr_val($field_name, 'error_message');
        $field_value = ($field_value) ? $field_value : $CI->input->post($field_name);

        switch($type)
        {
            case 'text':
                $print = '&lt;input type="text" name="' . $field_name . '" value="' . $field_value . '" title="' . $title . '" ' . $this-&gt;_print_attributes($field_name, $field_attributes) . '  />';
            break;
        }

        return $print;
    }
    
    function _print_attributes($field_name, $field_attributes)
    {
        /* the class attribute should be the only real unique attribute here, b/c the JS library is looking for the meta data within the class */
        foreach($field_attributes as $attr_name => $attr_value)
        {
            switch($attr_name)
            {
                case 'class':
                    $print = 'class="' . $attr_value . ' {' . rtrim($this->_ci_to_jquery($this->_get_attr_val($field_name, 'rules')), ',') . '}"';
                break;
                default:
                    $print = $attr_name . '=' . $attr_value;
                break;
            }
            
            return $print;
        }  
    }
    
    function _ci_to_jquery($rules)
    {
        /* jQuery's form validation rule titles are slightly different than CI's, so lets match them up.
        since they are different, we probably won't end up with a match for every rule in the end.
        */
        $print = NULL;
        $rules = explode('|', $rules);
        foreach($rules as $rule)
        {
            switch($rule)
            {
                case 'required':
                    $print .= 'required: true,';
                break;
                case 'valid_email':
                    $print .= 'email: true,';
                break;
            }
        }        
        
        return $print;
    }
    
    function _get_attr_val($field_name, $attr_name)
    {
        return $this->forms[$this->current_form][$field_name][$attr_name];
    }
    
    
}

?&gt;
#2

[eluser]litzinger[/eluser]
home_controller.php

Code:
&lt;?php
class Home extends Controller {

    function Home()
    {
        parent::Controller();
        $this->load->config('site_settings');
        $this->load->library(array('harmony','validation'));
        $this->load->helper(array('url','form'));    
    }
    
    function index()
    {
        $this->load->view('home_view');
    }
    
    function post()
    {
        $forms = $this->config->item('harmony_forms');
        $current_form = $forms['test_form'];

        foreach($current_form as $name => $attributes) {
            $rules[$name] = $attributes['rules'];
        }

        $this->validation->set_rules($rules);
        $this->validation->set_error_delimiters('<li>','</li>');

        foreach($current_form as $name => $attributes) {
            $fields[$name] = $attributes['label'];
        }
        
        $this->validation->set_fields($fields);

/* this is where i'd like to be able to set the error messages to what is in the site_setttings file */
              
        
        if ($this->validation->run() == FALSE) {
            $data['post'] = $_POST;
            $data['message'] = 'failed';
            $this->load->view('home_view', $data);
        } else {
            $data['post'] = $_POST;
            $data['message'] = 'success';
            $this->load->view('home_view', $data);
        }

        
    }
}
?&gt;


home_view.php
Code:
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Welcome to CodeIgniter&lt;/title&gt;

config->item('base_url') ?&gt;scripts/jquery.js">
config->item('base_url') ?&gt;scripts/jquery.metadata.js">
config->item('base_url') ?&gt;scripts/jquery.validate.pack.js">

&lt;/head&gt;
&lt;body&gt;

&lt;?php
$attributes = array('class' => 'validateForm', 'name' => 'test_form');
echo $this->harmony->pre_form($attributes['class'], $attributes['name'], 'error');
echo form_open("home/post", $attributes);
?&gt;
<fieldset>

Name: &lt;?php echo $this->harmony->input('name', '', array('class' => 'text_field', 'id' => 'name_123')) ?&gt;<br />

Email: &lt;?php echo $this->harmony->input('email', '', array('class' => 'text_field', 'id' => 'email_123')) ?&gt;<br />

&lt;input type="submit" name="submit" value="Submit" /&gt;

</fieldset>
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;

home_view.php rendered HTML

Code:
$(function(){  
                        $("form.validateForm").validate({
                            focusInvalid: true,
                            errorLabelContainer: $("ol.error"),
                            wrapper: "li"
                        });
                    })
                <ol class="error"></ol>&lt;form action="/harmony/index.php/home/post" method="post" class="validateForm" name="test_form"&gt;<fieldset>

Name: &lt;input type="text" name="name" value="" title="Please enter your first name" class="text_field {required: true}"  /&gt;<br />

Email: &lt;input type="text" name="email" value="" title="Please enter a valid email address" class="text_field {required: true,email: true}"  /&gt;<br />

&lt;input type="submit" name="submit" value="Submit" /&gt;

</fieldset>
&lt;/form&gt;
#3

[eluser]Phil Sturgeon[/eluser]
I like it so far, but wouldnt this make a kickass extention to the normal validation library? You can then use form helpers and use the exact same rules as your normal validation lib. Just pass the rules into the function and make your code create some auto-javascript for inserting into the page.
#4

[eluser]litzinger[/eluser]
I thought about the extension, though I'm not sure how to do that just yet. I'll have to play around with it. This was just a proof of concept for me to see if it was worth exploring more.
#5

[eluser]Phil Sturgeon[/eluser]
In response to your private message (so others could perhaps use the idea):

hey, thats not quite what i meant. With spry (adobes js lib) i had the idea of using rules from the CI validation to convert into a set of rules in Spry. This would be a case of exploding the rules on | then matching ci rules and values to spry rules. Then with a modified form helper i could make it add <span id="somethingField"> &lt;input&gt;</span> so the spry rules can pick up whats what.

You'd need some extra rules added to validation class but i have already made most, greater_than[], equal_to[], not_equal_to[] etc.

Would replace two files and would only require a user to insert one variable, &lt;?=$spry_validation;?&gt;.
#6

[eluser]Michael Wales[/eluser]
I love this idea. I always develop my applications with nothing bu normal HTTP requests in mind, that way I know it works for everyone. Then I go back and add all of the client-side validation, AJAX requests, etc.

Would be nice to have a few helpers/libraries out there that would speed this process up and keep us from having to duplicate our efforts.




Theme © iAndrew 2016 - Forum software by © MyBB