Hi,
ich bin gerade dabei ein mehrstufiges E-Mailformular zu erstellen, dessen Eingabefelder den Eigenschaften (Properties) eines Domain Models entsprechen.
Ich habe an folgende Anleitung gehalten:
http://forge.typo3.org/projects/typo3v4-mvc/wiki/How_to_use_partial_validation_for_a_multi-step_form
sowie
http://lists.typo3.org/pipermail/typo3-project-typo3v4mvc/2011-May/009643.html
Wenn ich das Formular in Schritt 2 mit leeren Eingabefeldern abschicke, dann erhalte ich FormErrors für alle Felder, also auch diejenigen, welche erst in Schritt 2 dran sind. Irgendwie greift diese partielle Valiedierung nicht 🙁
Hat das schon mal einer erfolgreich umgesetzt und könnte mir helfen?
Das Domain Model MyDomainModel sieht gekürzt so aus (hier stehen auch alle Validierungsregeln mittels Annotation @validate):
<?php
class Tx_MyExt_Domain_Model_MyDomainModel extends Tx_Extbase_DomainObject_AbstractEntity {
/**
* firstName
*
* @var string
* @validate notEmpty
*/
protected $firstName;
/**
* __construct
*
* @param array $data
*/
public function __construct() {
}
/**
* Returns the first name
*
* @return string $firstName
*/
public function getFirstName() {
return $this->firstName;
}
/**
* Sets the first name
*
* @param string $firstName
* @return void
*/
public function setFirstName($firstName) {
$this->firstName = $firstName;
}
}
?>
Die ExtendedValidatorResolver Klasse (siehe Download ersten Link) liegt im Verzeichnis ext_key/Classes/Valdation/ExtendedValidatorResolver.php und sieht so aus:
<?php
class Tx_MyExt_Validation_ExtendedValidatorResolver extends Tx_Extbase_Validation_ValidatorResolver {
/**
* Builds a base validator conjunction for the given data type.
*
* The base validation rules are those which were declared directly in a class (typically
* a model) through some @validate annotations on properties.
* It will only validate the properties which are sent with the current request.
*
*
* @param string $dataType The data type to build the validation conjunction for. Needs to be the fully qualified object name.
* @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL
*/
public function buildBaseValidatorConjunctionWithRequestData($dataType, $rawRequest) {
// Model based validator
// This method looks almost exactly like "buildBaseValidatorConjunction" in ValidatorResolver.
if (strstr($dataType, '_') !== FALSE && class_exists($dataType)) {
$objectValidator = $this->createValidator('GenericObject');
$validatorCount = 0;
foreach ($this->reflectionService->getClassPropertyNames($dataType) as $classPropertyName) {
$classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($dataType, $classPropertyName);
if (!isset($classPropertyTagsValues['validate'])) continue;
// This is the only real change to the ValidatorResolver: We only add the
// validation for a property if this property has been changed in the current request.
if (!is_array($rawRequest) || !isset($rawRequest[$classPropertyName])) continue;
foreach ($classPropertyTagsValues['validate'] as $validateValue) {
$parsedAnnotation = $this->parseValidatorAnnotation($validateValue);
foreach ($parsedAnnotation['validators'] as $validatorConfiguration) {
$newValidator = $this->createValidator($validatorConfiguration['validatorName'], $validatorConfiguration['validatorOptions']);
if ($newValidator === NULL) {
throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $dataType . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorConfiguration['validatorName'] . '".', 1241098027);
}
$objectValidator->addPropertyValidator($classPropertyName, $newValidator);
$validatorCount ++;
}
}
}
if ($validatorCount > 0) return $objectValidator;
}
return $this->createValidator('Conjunction'); // Just a validator returning TRUE; in case no validation needs to take place.
}
}
?>
Davon abgeleitet die Subclass (siehe ersten Link), welche dann vom eigentlichen Controller geerbt wird:
<?php
class Tx_MyExt_Controller_MultiStepFormController extends Tx_Extbase_MVC_Controller_ActionController {
/**
* 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_MyExt_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);
}
}
}
?>
Und hier der Crontoller zum Domain Model MyDomainModel von oben:
<?php
class Tx_MyExt_Controller_MyDomainModelController extends Tx_MyExt_Controller_MultiStepFormController {
/**
* Show Step 1
*
* @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
* @dontvalidate $myDomainModel
*
* @return string A HTML Form
*/
public function showStep1Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel=NULL) {
$this->view->assign('myDomainModel', $myDomainModel);
}
/**
* Save Step 1
*
* @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
*
* @return string A HTML Form
*/
public function saveStep1Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
$this->redirect('showStep2');
}
/**
* Initialize Save Step 1
*
* @return void
*/
public function initializeSaveStep1Action() {
$this->registerPartialValidatorForArgument('firstName');
$this->registerPartialValidatorForArgument('lastName');
$this->registerPartialValidatorForArgument('birthdayDay');
$this->registerPartialValidatorForArgument('birthdayMonth');
$this->registerPartialValidatorForArgument('birthdayYear');
$this->registerPartialValidatorForArgument('company');
$this->registerPartialValidatorForArgument('street');
$this->registerPartialValidatorForArgument('postalCode');
$this->registerPartialValidatorForArgument('city');
$this->registerPartialValidatorForArgument('email');
}
/**
* Show Step 2
*
* @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
* @dontvalidate $myDomainModel
*
* @return string A HTML Form
*/
public function showStep2Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
$this->view->assign('myDomainModel', $myDomainModel);
}
/**
* Save Step 2
*
* @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
*
* @return string A HTML Form
*/
public function saveStep2Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
$this->redirect('submit');
}
/**
* Initialize Save Step 2
*
* @return void
*/
public function initializeSaveStep2Action() {
$this->registerPartialValidatorForArgument('agb');
}
/**
* Show Submit View
*
* @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
* @dontvalidate $myDomainModel
*
* @return string A HTML Form
*/
public function submitAction(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
$this->view->assign('myDomainModel', $myDomainModel);
}
}
?>
Schaut man sich die initializeSaveStep*Action Methoden an, dann sieht man, dass das Feld agb erst in Schritt 2 validiert werden soll, wie jedoch oben beschrieben erhalte ich schon in Schritt 1 die Validierungsfehlermeldungen für alle im Domain Model mit der Annotation @validate versehenen Eigenschaften:
firstName: The given subject was empty.
lastName: The given subject was empty.
birthdayDay: The given subject was empty.
birthdayMonth: The given subject was empty.
birthdayYear: The given subject was empty.
company: The given subject was empty.
street: The given subject was empty.
postalCode: The given subject was empty.
city: The given subject was empty.
email: The given subject was empty.
agb: The given subject was NULL. <--- Das dürfte erst in Schritt 2 validiert werden!
Die Fluid Templates erspar ich euch jetzt mal (falls ihr sie braucht/wollt werd ich die nachreichen).
Ich hoffe ihr könnt mir weiterhelfen 🙂