CakePHP validation on a Search Form

I wanted to validate the fields on a search form (e.g. date, price) like the add/edit forms work when you bake or use the scaffolding.

Why is this a problem.

CakePHP has great built in validation but it is tied into saving data.
If you have a search form on your website, nothing needs to be saved and there probably isn’t even a suitable Model anyway.
Another issue is that I wanted it to work where my index view contains the search form and this posts to the results action. The results action processes the search and the view displays the search results. If there’s a validation error you need to go back to the index view.

I did consider just using jQuery to validate the form contents before it went up to the server – but I’m sure you all know that it is poor practice to just use client side validation.

To validate like the add or edit scaffolding the view needs to submit to its own action just like they do.
However that would mean the search results would also be displayed within the same view.

  1. I could run the query and then render a different view (but then the URL won’t change).
  2. I could store the search params in a session and redirect to the results view, then retrieve the search params, run the search and display it.

I began to worry how well pagination would work with either of these options.

I decided to try this with the way I wanted it to work – the search view submitting to a different action which displays the results, then if invalid I just needed a way of displaying the search form again and highlighting the errors.

The trick is to store validation messages and posted form data in Sessions and redirect back to the search form.

First I considered reading the Session in the view and injecting into CakePHP variables.
The code below shows how to add validation information within a view:

<?php
$this->validationErrors['Category']['name'][0] = 'Validation message text here';
$this->validationErrors['Category']['name'][0] = 'Price should be < 100';
$this->request->data['Category']['name'] = 'Form field data in here';
$this->request->data['Category']['price'] = 123;
?>

In practice it is easier to inject those messages in the Controller.
Many thanks for this blog article which showed me how to do it:
http://www.jamesfairhurst.co.uk/posts/view/validating_cakephp_data_from_another_model/

In the controller action which receives form input you do the following:

$post = $this->request->data['Category'];
if (empty($post['search_text'])) {
	$this->Category->validationErrors['search_text'][0] = 'You must provide search text.';
}
if (empty($post['price'])) {
	$this->Category->validationErrors['resort_id'][0] = 'You must provide a price.';
}
 
if (count($this->Category->validationErrors) > 0) {
	$this->Session->write('Category', $this->data);
	$this->Session->write('CategoryErrors', $this->Category->validationErrors);
	$this->redirect(array('action' => 'index'));
}

This checks for errors, stores the errors and data in sessions and redirects back to the search form.

And in the controller action which displays the form:

//see if there were any validation problems
if ($this->Session->check('Category')) {   
	// get data the user posted   
	$chalet = $this->Session->read('Category');   
	// get the errors   
	$errors = $this->Session->read('CategoryErrors');   
	// set their data for view   
	$this->request->data['Category'] = $chalet['Category'];   
	// set validation errors for view   
	$this-> Category->validationErrors = $errors;   
	// delete the session data   
	$this->Session->delete('Category');   
	$this->Session->delete('CategoryErrors');   
}

This breaks CakePHP’s principles of Fat Models and Skinny Controllers (maybe I could move some of this code into Model functions) but I’d be very interested to see what other ways there are of doing this.

3 Replies to “CakePHP validation on a Search Form”

  1. I think you are making it more complicated.
    You can use the model validation rules even for nonexisting (database) fields.
    those rules can be applied just like the others and will work flawlessly.
    They just won’t save (of course) – but thats usually fine 🙂

    So simply use “search” and “some_field”, create validation rules for them and call it from the controller with $this->Model->set() and $this->Model->validates()

    1. Thanks for sharing this. I’m trying to implement form validators for my search form too. So far this is the only solution I have come across but I am reluctant to use it because it doesn’t seem very elegant. I’m using CakePHP 1.3.

      The problem with Mark’s (first commentor) approach is that in my case, my search form has a postcode field which can be left empty but if a value is entered I want to apply the postcode validation rule. This same field in the add form cannot be left empty and has to be validated against the postcode rule.

      In summary what I want to know is whether it is possible to apply different validation rules to the same field. I understand that validation rules can only be applied via the model class and these are enforced when the save method is called so I’m assuming its not possible to apply a validation rule from within the controller action?

      1. The solution I came across is simple as my search form has fields corresponding to the model that I’m searching in. This allows me to use CakePHP’s built in validation helper.

        Therefore in the search action of the controller I define a different set of validation rules for use within that action hence it overrides the ones I defined in the model which are called upon save. My code in the search action looks something like this:


        $this->Outlet->set($this->data);

        // define a custom validation for this form, we don't want to use the same validation rules as we do when we save outlet
        $validate = array(
        'postcode' => array(
        'rule' => array(
        'postal', null, 'uk'
        ),
        'allowEmpty' => true
        )
        );
        $this->Outlet->validate = $validate;

        if ($this->Outlet->validates()) {
        // code to process search
        }

        Hope this helps anyone who is faced with a similar scenario.

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA - answer the question below to prove you are human *