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.
- I could run the query and then render a different view (but then the URL won’t change).
- 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:
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.