Thursday, 14 August 2014

Validating CakePHP Data From Another Model

n this article I'm going to go through the steps of validating CakePHP data from another Model. More specifically if you are building a Blog Application this article will demonstrate a way of validating Comment data if the add comment form is on the Post view Page.
This is something which pops up frequently on the CakePHP Google Group and is something that I've always wanted to achieve but never got round to coding a solution.

The View

Below is a cutdown version of my Posts view.ctp, it will display the Post and also display a form that a user can input a Comment. I'm going to save the Post Id as a hidden field and I'm also going to pass an extra argument in the url so the Comments Controller knows that the request came from the Posts View Page. This is simply passing in the action of the form e.g. 'action'=>'add/post'
// file: app/views/posts/view.ctp

<div class="posts view">
// display the actual post data
</div>

<div class="related">
 <h3>Comments</h3>
 <?php echo $form->create('Comment',array('action'=>'add/post', 'id'=>'PostsAddComment'));?>
  <legend>Add Comment</legend>
  <?php echo $form->hidden('post_id', array('value'=>$post['Post']['id'])); ?>
  <?php echo $form->input('name'); ?>
  <?php echo $form->input('email'); ?>
  <?php echo $form->input('url'); ?>
  <?php echo $form->input('body'); ?>
  <div class="input buttons">
   <input type="submit" name="submit" value="submit" />
  </div>
 <?php echo $form->end();?>
</div>

Comments Controller

The method of doing validating another Model's data is to save the data and validation errors into the Session and then redirect to the appropriate page. In this instance if the Comment was saved successfully and the referrer equals "post" then I redirect to the Posts View URL. If there was an error we save the Comment Data and Validation Errors into the Session and redirect back to the Post View Page.
Once this has been done we simply need to check the Session for existing Comment data in the Posts Controller and save it to the view so that the form displays the correct errors.
// file: app/controllers/comments_controller.php

function add($referer = null) {
 // if data not empty
 if (!empty($this->data)) {
  // init comment
  $this->Comment->create();
  // get post
  $this->Comment->Post->recursive = -1;
  $post = $this->Comment->Post->find('first',array(
   'conditions'=>array('Post.id'=>$this->data['Comment']['post_id'])
  ));

  // try saving comment
  if ($this->Comment->save($this->data)) {
   // set flash
   $this->Session->setFlash(__('The Comment has been saved', true));
   // if refererer is post
   if($referer == 'post') {
    // redirect to post view
    $this->redirect(array('controller'=>'blog', 'action'=>'view/'.$post['Post']['slug']));
   } else {
    $this->redirect(array('action'=>'index'));
   }
  } else {
   // set flash
   $this->Session->setFlash(__('The Comment could not be saved. Please, try again.', true));
   // if refererer is post
   if($referer == 'post') {
    // save comment data
    $this->Session->write('Comment', $this->data);
    // save comment errors
    $this->Session->write('CommentErrors', $this->Comment->validationErrors);
    // redirect back to post view
    $this->redirect(array('controller'=>'blog', 'action'=>'view/'.$post['Post']['slug']));
   }
  }
 }
 // get posts
 $posts = $this->Comment->Post->find('list');
 $this->set(compact('posts'));
}

Comments Model

This is my Comments Model with some sample validation rules for you to use.
// file: app/models/comment.php
<?php
class Comment extends AppModel {
 var $name = 'Comment';
 var $belongsTo = array(
  'Post' => array(
   'className'=>'Post',
   'conditions'=>'Post.status=1',
  )
 );
 var $validate = array(
  'name' => array('required'=>VALID_NOT_EMPTY),
  'email' => array('rule'=>'email','message'=>'A valid email address is required'),
  'url' => array('rule'=>'url','message'=>'A valid url is required'),
  'body' => array('required'=>VALID_NOT_EMPTY)
 );
}
?>

Posts Controller

In the Posts Controller for the View action I check the Session for Comment data, if it exists then I save the Data and Validation Errors into the View and finally delete them from the Session to ensure that the data is fresh when I resubmit.
I dont know the finer details of CakePHP Validation but if any errors occured during the Validation then they are placed in the $this->Model->validationErrors variable and so if I save these between Controllers using the Session I can successfully persist the data and display the correct errors messages depending on the Models validation rules.
// file: app/controllers/posts_controller.php

function view($id = null) {
 // if comment data is set
 if($this->Session->check('Comment')) {
  // get comment data
  $comment = $this->Session->read('Comment');
  // get comment errors
  $errors = $this->Session->read('CommentErrors');
  // set comment data for view
  $this->data['Comment'] = $comment['Comment'];
  // set validation errors for view
  $this->Post->Comment->validationErrors = $errors;
  // delete comment data
  $this->Session->delete('Comment');
  $this->Session->delete('CommentErrors');
 }
 // check post is valid
 $post = $this->_check_post($id);
 // set post for view
 $this->set('post', $post);
}

No comments:

Post a Comment