Welcome Guest, Not a member yet? Register   Sign In
AJAX and form validation
#1

[eluser]D_Williams[/eluser]
I'm developing a custom reporting system for work using CI. After much tinkering I believe I've finally come up with a good implementation that should be secure, powerful, and extensible.

Before I explain my problem, let me try to sum up how it works without just dumping a bunch of code all over the place.

I have one main "reporting" controller. It has an index function that loads in the main report interface view. This view starts out simply displaying a bunch of links for every report in the system. The controller also has two AJAX only functions, called get_config() and run_report(). Somewhat obviously, get_config() loads in a view containing the report configuration form (asking for date ranges, client names, or whatever other data the report needs). run_report() is a fairly simple function, it determines what report is being requested and how the user wants it (display in page, download as PDF, or save to database are my current options). The report logic is stored in private methods of the reporting controller. One method for each report. run_report() calls the appropriate report method and does whatever is requested with the output.

Now, this isn't all implemented yet. Currently I have the get_config() part pretty much done and it works well. When a user clicks a report on the report list, it makes an AJAX call to get_config() which loads the appropriate view and returns the form inputs through AJAX, and they are displayed correctly. When the user fills out the report configuration and hits submit, it is correctly submitted via AJAX to run_report().

The obvious first step in run_report() is to validate and sanitize that form input. I'm a bit at a loss on how to do that, though. My first problem here is that every report has dynamic configuration options, so in run_report() I don't see any way to even determine what checks should be made. The second is that the CI form validation class doesn't seem to work here. It works great for "normal" form posts, but everything here is being posted behind the scenes in AJAX. I googled a bit and found some AJAX form validation classes, but if I'm understanding them correctly, they are all javascript client-side checks. I don't want to do that, I want to check it with PHP for obvious reasons. I also want a generalized and flexible solution here so that I don't have to write a bunch of form validation rules for every single report.

One note, if it's relevant here, is that I have a custom library that standardizes all report config fields. For example, here's one of my report config views:

Code:
<input type="hidden" name="report" value="account_acknowledgement" />
<b>Start Date:</b> &lt;?php echo $this->form_elements->CalendarSelect('startdate'); ?&gt;<br />
<b>End Date:</b> &lt;?php echo $this->form_elements->CalendarSelect('enddate'); ?&gt;<br />
<b>Client:</b> &lt;?php echo $this->form_elements->ClientSelect('client'); ?&gt;

form_elements is my custom library, and it has a handful of functions that return HTML for my semi-complex controls so they're the same in every report. For example, ClientSelect() queries the database for all our clients and returns a drop-down box. Each one of them takes one argument: the input element's name.

I have a habit of designing myself into a hole. Have I done it again? Does anyone have any nice solutions or advice here?
#2

[eluser]D_Williams[/eluser]
Bump
#3

[eluser]SPeed_FANat1c[/eluser]
Quote:The obvious first step in run_report() is to validate and sanitize that form input. I’m a bit at a loss on how to do that, though. My first problem here is that every report has dynamic configuration options, so in run_report() I don’t see any way to even determine what checks should be made.

Don't know if its the best way but I think you could use hidden input fields to store configuration, what checks should be made.

Quote:The second is that the CI form validation class doesn’t seem to work here.

When you sumbit with ajax, you pass data to function via post? I mean when you enter the data to database you use $this->input->post() ?
#4

[eluser]Madmartigan1[/eluser]
Quote:The second is that the CI form validation class doesn’t seem to work here.

Not 100% sure what you're doing (tl;dr) but if you post, via ajax, to the same controller that normally validates the form, you should be fine. Have the controller return something different if the request is ajax, like some json with the form errors and a flag that it didn't validate.
#5

[eluser]D_Williams[/eluser]
[quote author="SPeed_FANat1c" date="1294095918"]
Quote:The obvious first step in run_report() is to validate and sanitize that form input. I’m a bit at a loss on how to do that, though. My first problem here is that every report has dynamic configuration options, so in run_report() I don’t see any way to even determine what checks should be made.

Don't know if its the best way but I think you could use hidden input fields to store configuration, what checks should be made.

Quote:The second is that the CI form validation class doesn’t seem to work here.

When you sumbit with ajax, you pass data to function via post? I mean when you enter the data to database you use $this->input->post() ?[/quote]

I don't really like the idea of using hidden fields in that manner. It makes the system seem less dynamic and requires a lot more overhead when creating my report config files.

And yes, in theory, I will use $this->input->post() to access the form elements sent by AJAX.

[quote author="Madmartigan1" date="1294100976"]
Quote:The second is that the CI form validation class doesn’t seem to work here.

Not 100% sure what you're doing (tl;dr) but if you post, via ajax, to the same controller that normally validates the form, you should be fine. Have the controller return something different if the request is ajax, like some json with the form errors and a flag that it didn't validate.[/quote]

Hmm upon re-examining the form validation class, it might work. I was used to the concept of it loading in views on success or fail but I guess technically I can just have it echo out errors upon failing.

That brings me back to the problem of dynamically detecting what form elements need to be validated. The first thing that pops into my head is having my form_elements functions automatically insert a prefix in the name so I could do some sort of wildcard in the validation rules:

Code:
$this->form_validation->set_rules('clientselect_*', 'client', 'rules|rules|rules');

But that isn't allowed as far as I know. If it worked like that, all my form elements starting with clientselect_ would be subject to the same validation rules.

Is there any other way to do this?
#6

[eluser]SPeed_FANat1c[/eluser]
You could post how exactly the form looks. I assume there is some checkboxes or something the controller can 'know' what is the configuration. So when user checks some checkbox there is one configuration, if the checkbox is unchecked, then there is another configurations. So in validation function you could just check what the configurations is with if statements

funtion validate()
{
if some checkbox checked
apply this validation rule
else
apply another validation rule

this->form->validation->run
}
So what about this?
#7

[eluser]D_Williams[/eluser]
[quote author="SPeed_FANat1c" date="1294147733"]You could post how exactly the form looks. I assume there is some checkboxes or something the controller can 'know' what is the configuration. So when user checks some checkbox there is one configuration, if the checkbox is unchecked, then there is another configurations. So in validation function you could just check what the configurations is with if statements

funtion validate()
{
if some checkbox checked
apply this validation rule
else
apply another validation rule

this->form->validation->run
}
So what about this?[/quote]

The problem is that there is no one form to post. In the finished project, there will be dozens of different reports with more potentially added all the time. I don't want to have some massive switch statement telling it what to do on each and every report, that's not flexible enough for me.

I've been pondering this for a while and I've come up with a possible idea of looping through $_POST's keys (since there's no way to loop through $this->input->post() values as far as I know) and emulating the "wildcard" idea I had in my previous post.

Something like this (not tested):
Code:
foreach(array_keys($_POST) as $key)
        {
            if(strpos($key, 'clientselect_') !== FALSE)
                $rules = 'rules|for|clientselect';
            elseif(strpos($key, 'calendarselect_') !== FALSE)
                $rules = 'rules|for|calendars';
            // And so on
            $this->form_validation->set_rules($key, $key, $rules);    
        }

Something like that should, in theory, set the validation rules for every form field it finds with a prefix dynamically without me having to tell it what's there. It would still be better if form validation rules could use wildcards and I could just put it into a config file, but I guess this will work. Does anyone have any thoughts on doing it this way?
#8

[eluser]Bramme[/eluser]
I think you're on the right way at the moment. Is it possible for you to have a config array for all the form fields that stores the validation rules?

If so, you could do something like (it's been a while since I've used the form_validation library, so I'm skipping that code)
Code:
$field_config = array(
    'report1' => array(
        'startdate' => array('type' => 'CalendarSelect', 'rules' => 'rules|for|calendar'),
        'enddate' => array('type' => 'CalendarSelect', 'rules' => 'rules|for|calendar'),
        'client' => array('type' => 'ClientSelect', rules => 'rules|for|client')
    ),
    'report2' => ...
);

foreach ($_POST as $key => $value)
{
    $report = 'report1';
    $rules = $field_config[$report][$key]['rules'];
    $this->form_validation->set_rules($key, $key, $rules);
}

Ideally, the $field_config array would be built from a database table, I guess.

Working like this, would also allow you to shorten your report-generation code a little, allowing you to do:
Code:
foreach ($field_config['report1'] as $key => $field)
{
    $function = $field['type'];
    $fieldname = $key;
    echo $this->form_elements->$function($fieldname);
}
#9

[eluser]D_Williams[/eluser]
@Bramme - That still requires more report-specific configuration than I'd like. Ideally I want to completely separate the report engine from the actual reports.

I believe the last bit of code I posted should mostly work in this situation. The only catch is that each config element has to have a "prefix" in order to be validated, but I can live with that.

For anyone interested, here's the code I ended up with (minus actual rules for input elements):
Code:
foreach(array_keys($_POST) as $key) // Loop through $_POST to determine what validation rules need to be set
            {
                    if(strpos($key, 'clientselect_') !== FALSE)
                        $rules = 'required|min_length[20]';
                    elseif(strpos($key, 'calendarselect_') !== FALSE)
                        $rules = 'required';
                    else continue;
                        
                    // Strip out the prefix to get the human friendly name
                    $prefixPos = strpos($key, '_'); // First underscore position
                    $name = ucwords(substr($key, $prefixPos + 1));
                        
                    // Set the rule
                    $this->form_validation->set_rules($key, $name, $rules);    
            }

It seems to work well enough from the bit I've tested it. I'll call this mission accomplished for now Big Grin




Theme © iAndrew 2016 - Forum software by © MyBB