Multiple Validation Sets in CakePHP

Jonathon Snook wrote about multiple validation sets in CakePHP and it reminded me how often we require rather different sets of validation rules for a single model. I have always enjoyed learning how others approached similar problems so I think I shall share mine too.

A very relevant example is the User model where we usually have multiple forms associated. E.g. sign up, profile updates, password reset, etc. Each form have a different set of fields while some share the same fields. My approach is to select a different set of fields for each “action” and modify the rules if required. This means I’ll keep a master set of rules for all the User model fields and call setValidate() for each “action”.

/**
 * Set the default validation rules here.
 *
 * @var array
 */
	protected $_validate = array(
		'username' => array(
			'empty' => array(
				'required' => true,
				'rule' => VALID_NOT_EMPTY,
				'last' => true
			),
			'invalid' => array(
				'rule' => '/^[\w]{3,30}$/',
				'last' => true,
				'on' => 'create'
			),
			'exists' => array(
				'rule' => 'validateUniqueUsername',
				'last' => true,
				'on' => 'create'
			)
		),
		'password' => array(
			'empty' => array(
				'required' => true,
				'rule' => VALID_NOT_EMPTY,
				'last' => true
			),
			'invalid' => array(
				'required' => true,
				'rule' => '/^.{6,30}$/',
				'last' => true
			)
		),
		...
	);
/**
 * Sets just enough fields for validation.
 *
 * @param array $fields List of field names to validate against. If no param
 *                      passed, all fields will be included.
 * @author Derick
 */
	function setValidate($fields = null) {
		if ($fields === null) {
			$this->validate = $this->_validate;
		} else {
			$this->validate = array();
			foreach ($fields as $f) {
				if (isset($this->_validate[$f])) {
					$this->validate[$f] = $this->_validate[$f];
				}
			}
		}
	}

This is a simplified example in my User model where I have the register() function to validate only the fields required for sign up and the updateProfile() function which uses another set of fields with slight different requirement.

function register($data) {
	$fields = array('username', 'password', ...);
	$this->setValidate($fields);
	$this->save($data, true, $fields);
}

function updateProfile($data) {
	$fields = array('password', ...);
	$this->setValidate($fields);

	// derick: allow user to left password field empty
	unset($this->validate['password']['empty']);
	$this->validate['password']['invalid']['allowEmpty'] = true;
	$this->save($data, true, $fields);

	// derick: you can also call this again to "reset" to the default set of
	// rules but usually we don't need this
	$this->setValidate();
}

In other words, I only update the $validate array in the model when required. Some might think it is dangerous since there is a chance of saving data without validation but this works for me. What do the rest of you do?