How to use partial validation for a multi-step form¶
(Sebastian Kurfürst)
This is a pragmatic approach to implementing partial validation in Extbase. It is NOT the final solution to this problem, that's why this will not become part of Extbase at the moment. Instead, the FLOW3 team is working on a more complete and consistent solution.
The Problem¶
Extbase validates all action arguments completely before calling an action. However, for a multi-step form, it makes sense to only validate those model properties which are changed in the current request.
The pragmatic solution¶
- Use the attached
ExtendedValidatorResolver.phpand place it somewhere in your extension. Make sure to adjust the class name inside the class accordingly. - Then, create a subclass of the
ActionControllerfrom which all the controllers in your extension which need this functionality extend:
Tx_Extbase_MVC_Controller_ActionController
/\
||
Tx_MyExtension_MyBaseController
/\
||
Tx_MyExtension_Controller_Controller*
- Inside this subclass, create a method
registerPartialValidatorForArgument(), as follows:
/**
* If you need a multi-step form which updates a domain object, you only want properties validated which are
* sent with the current request. This method should be called in the corresponding initialize*Action,
* and it will rebuild the registered validators for this argument.
*
* It will also respect the @validate annotation on the action method name.
*
* THIS METHOD WILL NOT CHECK A @dontvalidate ANNOTATION. Thus, it should NOT be used
* for displaying a form, but instead be used for SAVING data.
*
* @param string $argumentName The name of the argument where the partial validator should be registered for.
*/
protected function registerPartialValidatorForArgument($argumentName) {
if ($this->request->hasArgument($argumentName)) {
// Initialize the extended validator resolver.
$extendedValidatorResolver = t3lib_div::makeInstance('Tx_BlogExample_Validation_ExtendedValidatorResolver');
$extendedValidatorResolver->injectObjectManager(t3lib_div::makeInstance('Tx_Extbase_Object_Manager')); // Singleton
$extendedValidatorResolver->injectReflectionService(t3lib_div::makeInstance('Tx_Extbase_Reflection_Service')); // Singleton
// Load all parameter validators and pick the one for the current argument, as this is the base validator.
$parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName);
$baseValidator = $parameterValidators[$argumentName];
// Build up the validator for all submitted data.
$rawRequestDataForArgument = $this->request->getArgument($argumentName);
$argument = $this->arguments[$argumentName];
$partialValidator = $extendedValidatorResolver->buildBaseValidatorConjunctionWithRequestData($argument->getDataType(), $rawRequestDataForArgument);
// Add the partial validator to the base validator; and override the validations of the argument.
$baseValidator->addValidator($partialValidator);
$argument->setValidator($baseValidator);
}
}
Usage¶
- Now, suppose you have a form which consists of two steps:
step 1 --> step 2
- For each step, create one action for showing the form, and one action for saving the form:
step 1 step2
showstep1Action
savestep1Action
redirect \-- showstep2action
savestep2action
- All
show*actions need to get a@dontvalidateannotation, as usual. - At the end of a
save*action, place aredirect()to the next showAction. - Now, for each
save*action, create an appropriateinitialize*Action. Example: forsavestep1Action, createinitializeSavestep1Action, and so on. - Inside this
initialize*Action, call the methodregisterPartialValidatorForArgumentfrom above, with the name of the argument for which partial validation should occur.
Sample¶
While reconstructing/research this technique I noticed, that there are somen pending requests in the web, so i decide to write a small example plugin.
You can find the plugin at http://typo3.org/extensions/repository/view/hh_multipageform_example/current/
I hope it helps (Heiko Hardt)