Partielle Formular Validierung (multistep)

  • Typo3Cuckoo Typo3Cuck...
    Sternenflotten-Admiral
    0 x
    184 Beiträge
    0 Hilfreiche Beiträge
    06. 10. 2011, 17:38

    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 [b]MyDomainModel[/b] sieht gekürzt so aus (hier stehen auch alle Validierungsregeln mittels Annotation @validate):

    1. <?php
    2. class Tx_MyExt_Domain_Model_MyDomainModel extends Tx_Extbase_DomainObject_AbstractEntity {
    3.  
    4. /**
    5. * firstName
    6. *
    7. * @var string
    8. * @validate notEmpty
    9. */
    10. protected $firstName;
    11.  
    12. /**
    13. * __construct
    14. *
    15. * @param array $data
    16. */
    17. public function __construct() {
    18. }
    19.  
    20. /**
    21. * Returns the first name
    22. *
    23. * @return string $firstName
    24. */
    25. public function getFirstName() {
    26. return $this->firstName;
    27. }
    28.  
    29. /**
    30. * Sets the first name
    31. *
    32. * @param string $firstName
    33. * @return void
    34. */
    35. public function setFirstName($firstName) {
    36. $this->firstName = $firstName;
    37. }
    38.  
    39. }
    40. ?>

    Die [b]ExtendedValidatorResolver[/b] Klasse (siehe Download ersten Link) liegt im Verzeichnis [b]ext_key/Classes/Valdation/ExtendedValidatorResolver.php[/b] und sieht so aus:

    1. <?php
    2. class Tx_MyExt_Validation_ExtendedValidatorResolver extends Tx_Extbase_Validation_ValidatorResolver {
    3. /**
    4. * Builds a base validator conjunction for the given data type.
    5. *
    6. * The base validation rules are those which were declared directly in a class (typically
    7. * a model) through some @validate annotations on properties.
    8. * It will only validate the properties which are sent with the current request.
    9. *
    10. *
    11. * @param string $dataType The data type to build the validation conjunction for. Needs to be the fully qualified object name.
    12. * @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL
    13. */
    14. public function buildBaseValidatorConjunctionWithRequestData($dataType, $rawRequest) {
    15.  
    16. // Model based validator
    17. // This method looks almost exactly like "buildBaseValidatorConjunction" in ValidatorResolver.
    18. if (strstr($dataType, '_') !== FALSE && class_exists($dataType)) {
    19. $objectValidator = $this->createValidator('GenericObject');
    20.  
    21. $validatorCount = 0;
    22.  
    23. foreach ($this->reflectionService->getClassPropertyNames($dataType) as $classPropertyName) {
    24. $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($dataType, $classPropertyName);
    25. if (!isset($classPropertyTagsValues['validate'])) continue;
    26. // This is the only real change to the ValidatorResolver: We only add the
    27. // validation for a property if this property has been changed in the current request.
    28. if (!is_array($rawRequest) || !isset($rawRequest[$classPropertyName])) continue;
    29.  
    30. foreach ($classPropertyTagsValues['validate'] as $validateValue) {
    31. $parsedAnnotation = $this->parseValidatorAnnotation($validateValue);
    32. foreach ($parsedAnnotation['validators'] as $validatorConfiguration) {
    33. $newValidator = $this->createValidator($validatorConfiguration['validatorName'], $validatorConfiguration['validatorOptions']);
    34. if ($newValidator === NULL) {
    35. throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $dataType . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorConfiguration['validatorName'] . '".', 1241098027);
    36. }
    37. $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
    38. $validatorCount ++;
    39. }
    40. }
    41. }
    42. if ($validatorCount > 0) return $objectValidator;
    43. }
    44. return $this->createValidator('Conjunction'); // Just a validator returning TRUE; in case no validation needs to take place.
    45.  
    46. }
    47. }
    48. ?>

    Davon abgeleitet die [b]Subclass[/b] (siehe ersten Link), welche dann vom eigentlichen Controller geerbt wird:

    1. <?php
    2. class Tx_MyExt_Controller_MultiStepFormController extends Tx_Extbase_MVC_Controller_ActionController {
    3.  
    4. /**
    5. * If you need a multi-step form which updates a domain object, you only want properties validated which are
    6. * sent with the current request. This method should be called in the corresponding initialize*Action,
    7. * and it will rebuild the registered validators for this argument.
    8. *
    9. * It will also respect the @validate annotation on the action method name.
    10. *
    11. * THIS METHOD WILL NOT CHECK A @dontvalidate ANNOTATION. Thus, it should NOT be used
    12. * for displaying a form, but instead be used for SAVING data.
    13. *
    14. * @param string $argumentName The name of the argument where the partial validator should be registered for.
    15. */
    16. protected function registerPartialValidatorForArgument($argumentName) {
    17.  
    18. if ($this->request->hasArgument($argumentName)) {
    19.  
    20. // Initialize the extended validator resolver.
    21. $extendedValidatorResolver = t3lib_div::makeInstance('Tx_MyExt_Validation_ExtendedValidatorResolver');
    22. $extendedValidatorResolver->injectObjectManager(t3lib_div::makeInstance('Tx_Extbase_Object_Manager')); // Singleton
    23. $extendedValidatorResolver->injectReflectionService(t3lib_div::makeInstance('Tx_Extbase_Reflection_Service')); // Singleton
    24.  
    25. // Load all parameter validators and pick the one for the current argument, as this is the base validator.
    26. $parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName);
    27. $baseValidator = $parameterValidators[$argumentName];
    28.  
    29. // Build up the validator for all submitted data.
    30. $rawRequestDataForArgument = $this->request->getArgument($argumentName);
    31. $argument = $this->arguments[$argumentName];
    32. $partialValidator = $extendedValidatorResolver->buildBaseValidatorConjunctionWithRequestData($argument->getDataType(), $rawRequestDataForArgument);
    33.  
    34. // Add the partial validator to the base validator; and override the validations of the argument.
    35. $baseValidator->addValidator($partialValidator);
    36. $argument->setValidator($baseValidator);
    37. }
    38. }
    39.  
    40. }
    41. ?>

    Und hier der Crontoller zum Domain Model [b]MyDomainModel[/b] von oben:

    1. <?php
    2. class Tx_MyExt_Controller_MyDomainModelController extends Tx_MyExt_Controller_MultiStepFormController {
    3.  
    4. /**
    5. * Show Step 1
    6. *
    7. * @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
    8. * @dontvalidate $myDomainModel
    9. *
    10. * @return string A HTML Form
    11. */
    12. public function showStep1Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel=NULL) {
    13. $this->view->assign('myDomainModel', $myDomainModel);
    14. }
    15.  
    16. /**
    17. * Save Step 1
    18. *
    19. * @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
    20. *
    21. * @return string A HTML Form
    22. */
    23. public function saveStep1Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
    24. $this->redirect('showStep2');
    25. }
    26.  
    27. /**
    28. * Initialize Save Step 1
    29. *
    30. * @return void
    31. */
    32. public function initializeSaveStep1Action() {
    33. $this->registerPartialValidatorForArgument('firstName');
    34. $this->registerPartialValidatorForArgument('lastName');
    35. $this->registerPartialValidatorForArgument('birthdayDay');
    36. $this->registerPartialValidatorForArgument('birthdayMonth');
    37. $this->registerPartialValidatorForArgument('birthdayYear');
    38. $this->registerPartialValidatorForArgument('company');
    39. $this->registerPartialValidatorForArgument('street');
    40. $this->registerPartialValidatorForArgument('postalCode');
    41. $this->registerPartialValidatorForArgument('city');
    42. $this->registerPartialValidatorForArgument('email');
    43. }
    44.  
    45. /**
    46. * Show Step 2
    47. *
    48. * @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
    49. * @dontvalidate $myDomainModel
    50. *
    51. * @return string A HTML Form
    52. */
    53. public function showStep2Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
    54. $this->view->assign('myDomainModel', $myDomainModel);
    55. }
    56.  
    57. /**
    58. * Save Step 2
    59. *
    60. * @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
    61. *
    62. * @return string A HTML Form
    63. */
    64. public function saveStep2Action(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
    65. $this->redirect('submit');
    66. }
    67.  
    68. /**
    69. * Initialize Save Step 2
    70. *
    71. * @return void
    72. */
    73. public function initializeSaveStep2Action() {
    74. $this->registerPartialValidatorForArgument('agb');
    75. }
    76.  
    77. /**
    78. * Show Submit View
    79. *
    80. * @param Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel
    81. * @dontvalidate $myDomainModel
    82. *
    83. * @return string A HTML Form
    84. */
    85. public function submitAction(Tx_MyExt_Domain_Model_MyDomainModel $myDomainModel) {
    86. $this->view->assign('myDomainModel', $myDomainModel);
    87. }
    88. }
    89. ?>

    Schaut man sich die [b]initializeSaveStep*Action[/b] Methoden an, dann sieht man, dass das Feld [b]agb[/b] 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:

    1. firstName: The given subject was empty.
    2. lastName: The given subject was empty.
    3. birthdayDay: The given subject was empty.
    4. birthdayMonth: The given subject was empty.
    5. birthdayYear: The given subject was empty.
    6. company: The given subject was empty.
    7. street: The given subject was empty.
    8. postalCode: The given subject was empty.
    9. city: The given subject was empty.
    10. email: The given subject was empty.
    11. 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 :)


  • 1
  • vizArt vizArt
    T3PO
    0 x
    24 Beiträge
    0 Hilfreiche Beiträge
    25. 11. 2011, 17:15

    Hi Typo3Cuckoo,

    da ich das gleiche Problem hatte, und irgendwann meinen "Denkfehler" gefunden hatte, hab ich ein kleines Beispiel-Plugin geschrieben. Dieses habe ich dem Artikel http://forge.typo3.org/projects/typo3v4-mvc/wiki/How_to_use_partial_validation_for_a_multi-step_form ergänzt.

    Prinzipiell liegt aber unser Fehler darin, dass wir dachten es müsse die Property in die registerPartialValidatorForArgument() - Methode übergeben werden.
    Dies ist jedoch falsch. Du übergibst sozusagen das "model-object".

    In Deinem Besipiel wäre das:

    1. <?php
    2. /**
    3.  * Initialize Save Step 1
    4.  *
    5.  * @return void
    6.  */
    7. public function initializeSaveStep1Action() {
    8. $this->registerPartialValidatorForArgument($myDomainModel);
    9. }
    10. ?>

    Die Technik von Sebastian erkennt selbst welche Properties übergeben wurden, und wendet darauf dann die validierung an.

    Ich hoffe ich konnte Dir helfen
    LG viz

  • 1