So, here’s the next part of my series about error handling with Extbase and Fluid. This time it’s about writing own validators. But at first I will explain when to use one by a simple example.
Imagine you are about to build a form that creates a new object, let’s say a frontend user. Probably the user needs an email address as a unique identifier to log in with later on. So the model has a property called email.
class Tx_ExtensionName_Domain_Model_ModelName extends Tx_Extbase_DomainObject_AbstractEntity
{
/**
* @var string
* @validate Tx_Extbase_Validation_Validator_EmailAddressValidator
*/
protected $email= '';
}
If you know my post about setting own error messages this example might look very familiar. As you can see, the property is a simple string and we already have the validate annotation to verify the given address. So far there’s nothing new. The validation ensures that the address itself is valid but how do we check if it’s unique inside our environment? That’s where validation classes come into play.
Whereas annotations validate a single property against mostly just one rule, validators are more powerful as they are able to run a whole set of more complex validations within a different context. Keeping it simple you can say that property annotations take care that an object itself is consistent during its lifetime. Validators instead are set in the context of controller actions that handle the whole object, e.g when passing a new object to a create action. Some code for a better understanding:
class Tx_ExtensionName_Controller_ModelNameController extends Tx_Extbase_MVC_Controller_ActionController {
{
/**
* @param Tx_ExtensionName_Domain_Model_ModelName $newObject
* @validate $newObject Tx_ExtensionName_Domain_Validator_ValidatorName
*/
public function createAction(Tx_ExtensionName_Domain_Model_ModelName $newObject)
{
$this->modelNameRepository->add($newObject);
}
}
It’s line five that defines that the new object has to be validated against the vaildation class. Let’s switch to the validator itself:
class Tx_ExtensionName_Domain_Validator_MyValidator extends Tx_Extbase_Validation_Validator_AbstractValidator
{
/**
* @var Tx_ExtensionName_Domain_Repository_ModelNameRepository
*/
protected $modelNameRepository;
/**
* @param Tx_ExtensionName_Domain_Repository_ModelNameRepository $modelNameRepository
* @return void
*/
public function injectModelNameRepository(Tx_ExtensionName_Domain_Repository_ModelNameRepository $modelNameRepository)
{
$this->modelNameRepository = $modelNameRepository;
}
/**
* @param Tx_ExtensionName_Domain_Model_ModelName $value
* @return bool
*/
public function isValid($value)
{
$this->errors = array();
if ($this->modelNameRepository->findByEmail($value->getEmail())->count() > 0) {
$this->errors['email'] = new Tx_Extbase_Validation_PropertyError('email', time());
$this->errors['email']->addErrors(array(new Tx_Extbase_Validation_Error('Email not unique', 84647862842)));
return false;
}
return true;
}
}
Now it becomes interesting. Actually the “isValid”-method acts as a constructor, so you can write validators with just that method if you do not need any other logic. In this case we need access to a repository so there also is an inject method. If you do not know what’s going on, search for dependency injection (DI) in general and how it’s implemented in Extbase (since 1.3) in particular.
Having access to the repository, we can check if there’s a frontend user with the given email address yet. If so set errors and return false, else return true. But let’s see what’s happening in line 26 and 27. Again, as I mentioned in my post about setting error messages, there is an error cascade beginning with a Tx_Extbase_MVC_Controller_ArgumentError that is related to the handled object, e.g. the new frontend user. That object contains a Tx_Extbase_Validation_PropertyError for every property that is not valid. And for each validation that fails there is a Tx_Extbase_Validation_Error that holds an error message to display in the form. So, if there’s a matching frontend user we add a Tx_Extbase_Validation_PropertyError to the error array first. Now we know that the email property isn’t valid but what exactly went wrong tells us the Tx_Extbase_Validation_Error we assign in line 27. It took me quite a while to discover how add errors but once you know it, it absolutely makes sense and is very convenient, too.
Perhaps I should drop some lines about the error codes at last. Well, as you see, I just used time() for the Tx_Extbase_Validation_PropertyError as I don’t care about the message. But the Tx_Extbase_Validation_Error should have a certain error code you can access its related message with. I did not find a convention about how to choose an appropriate number but I recommend to take something very high or low and just increase the number by one for every validation error you create. I guess, I do not have to mention that you should not overwrite existing error codes, do I?
Useful links:
- http://en.wikipedia.org/wiki/Dependency_injection
- http://forge.typo3.org/projects/typo3v4-mvc/wiki/Dependency_Injection_%28DI%29
- Tx_Extbase_Validation_Validator_AbstractValidator
And as usual: Found a mistake? Have a question?
Just leave a reply!