By Sebastiaan de Jonge, published on Thursday, January 17, 2013 at 10:00

I love developing with Extbase! But when it comes to validation of form fields that don't fit with any domain model (for example, a CAPTCHA or a password confirmation), it's not that easy to do it in a neat way. Here's a solution I came up with.

The documentation shows...

In the documentation I've seen them fix this problem by simply adding a dummy property to the domain model, which isn't mapped to the database so it won't be stored. Now while this solution works fine, it's not really a neat way to solve the problem because you are polluting the domain model.

/**
 * CAPTCHA
 *
 * @var string
 * @validate NotEmpty, Tx_MyExt_Validation_Validator_CaptchaValidator
 */
protected $captcha;

The alternative

After quite some digging and looking through the Extbase source code, I came up with a different approach. I still believe that validation should be done through the official method, using validators. However data we are validating is not bound to any model so therefore not available in such. So instead we will bind our validators inside the controller.

Since our controller has access to the arguments that were posted along with the model parameters, we can now bind these to a validator (using setOptions). All we have to find now is where validators are added inside the controller. After some more digging I found out we can use the protected function called "initializeActionMethodValidators".

Example

If we put this into practice, we can see that this way we can add any amount of validators. Until now the only downside is that these are added at the end of the process. If you wish to do this differently you will need to rewrite the entire function (which is not very big by the way).

Below is an example of the function in my case, where it's validating my CAPTCHA. The object in this case is called 'Foo' and I've given the non-property fields the prefix "validation".

/**
 * Initialize action method validators
 *
 * @return void
 */
protected function initializeActionMethodValidators() {
	parent::initializeActionMethodValidators();
	foreach ($this->arguments as $argument) {
		/* @var  Tx_Extbase_MVC_Controller_Argument $argument */
		if ($argument->getName() == 'newFoo' && $this->actionMethodName == 'createAction') {
			/* @var Tx_Extbase_Validation_Validator_ConjunctionValidator $validator */
			$validator = $argument->getValidator();
			$requestArguments = $this->request->getArguments();
			// Add the CAPTCHA validator
			/* @var Tx_MyExt_Validation_Validator_CaptchaValidator $captchaValidator */
			$captchaValidator = t3lib_div::makeInstance('Tx_SazQuestions_Validation_Validator_CaptchaValidator');
			$captchaValidator->setOptions(
				array(
					'value' => $requestArguments['validation']['captcha']
				)
			);
			$validator->addValidator($captchaValidator);
		}
	}
}

Since we can use setOptions this way to add the arguments that are passed on to the controller, we can now access these inside our validator. My CAPTCHA validator was setup in the following way (I'm using the CAPTCHA ViewHelper).

public function isValid($value) {
	$this->errors = array();
	/* @var Tx_CaptchaViewhelper_Captcha $captcha */
	$captcha = t3lib_div::makeInstance('Tx_CaptchaViewhelper_Captcha');
	// Override the value
	if (!empty($this->options['value'])) {
		$value = $this->options['value'];
	}
	try {
		if ($value !== $captcha->getTextInSession()){
			$this->addError('Text is wrong.', 170320111501);
			return FALSE;
		}
	} catch(Exception $e) {
		t3lib_div::devLog ('captcha error: ' . $e->getMessage (), 'captcha_viewhelper', 2 );
		return FALSE;
	}
	return TRUE;
}

Comments