Validating CakePHP Data From Another Model

In 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);
}

Wrapping Up

Although I've wondered about this for quite some time the solution is quite easy although rather crude. But hey its works like a charm and you can successfully validate you data from other Models. Any problems leave a comment

Posted on 29th August 2008
on 29/8/08

comments powered by Disqus