Changeset 1444

Show
Ignore:
Timestamp:
12/25/06 23:39:25 (2 years ago)
Author:
dominik
Message:

BREAKING CHANGE!
- Reworked the internal error handling in the validation. Now validators have multiple incidents (usually one per failed internal run [one validator internally runs multiple times when you give it a base and it has to validate more then one field that way). An incident can have multiple errors, which can span multiple fields.
- Removed AgaviRequest::removeError (this a) doesn't work anymore and b) was a quite questionable thing to do in your code imho)
- Adjusted error retrieval/setting methods in AgaviRequest? to work with the new system. (please note that the only(!) reliable way to check if the validation failed is AgaviRequest::hasErrors(), the other methods will return the errors with the notice severity too and getErrorNames and hasError will actually return the fields with severity none as well)
- removed getRequest and reportError from validator containers
- fixed operator validators not storing their name
- added new result for not executed validators
- renamed hasAllArgumentsSet to checkAllArgumentsSet
- removed getAffectedFields completely (this currently makes the affects parameter of a validator useless)
- fixed the isset validator when used with a base and when the field was empty
- a whole lot of new methods for the validatorManager

refs #367

Location:
branches/0.11
Files:
2 added
8 modified

Legend:

Unmodified
Added
Removed
  • branches/0.11/src/request/AgaviRequest.class.php

    r1239 r1444  
    9696  public function getError($name) 
    9797  { 
    98     $errors = $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
    99     $retval = null; 
    100  
    101     if(isset($errors[$name]['messages'][0])) { 
    102       $retval = $errors[$name]['messages'][0]; 
    103     } 
    104  
    105     return $retval; 
     98    $vm = $this->getContext()->getValidatorManager(); 
     99    $incidents = $vm->getFieldIncidents($name, AgaviValidator::NOTICE); 
     100 
     101    if(count($incidents) == 0) { 
     102      return null; 
     103    } 
     104 
     105    $errors = $incidents[0]->getErrors(); 
     106    return $errors[0]->getMessage(); 
    106107  } 
    107108 
     
    117118  public function getErrorNames() 
    118119  { 
    119     $errors = $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
    120     if(isset($errors[''])) { 
    121       unset($errors['']); 
    122     } 
    123     return array_keys($errors); 
     120    return $this->getContext()->getValidatorManager()->getFailedFields(); 
    124121  } 
    125122 
     
    139136  public function getErrors($name = null) 
    140137  { 
    141     $errors = $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
     138    $vm = $this->getContext()->getValidatorManager(); 
     139    $errors = array(); 
     140 
     141    foreach($vm->getIncidents(AgaviValidator::NOTICE) as $incident) { 
     142      $validator = $incident->getValidator(); 
     143      foreach($incident->getErrors() as $error) { 
     144        $msg = $error->getMessage(); 
     145        foreach($error->getFields() as $field) { 
     146          if(!isset($errors[$field])) { 
     147            $errors[$field] = array('messages' => array(), 'validators' => array()); 
     148          } 
     149          $errors[$field]['messages'][] = $msg; 
     150          if($validator) { 
     151            $errors[$field]['validators'][] = $validator->getName(); 
     152          } 
     153        } 
     154      } 
     155    } 
     156 
    142157    if($name === null) { 
    143158      return $errors; 
     
    161176  public function getErrorMessages($name = null) 
    162177  { 
    163     $errors = $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
     178    $vm = $this->getContext()->getValidatorManager(); 
    164179 
    165180    if($name !== null) { 
    166       return isset($errors[$name]['messages']) ? $errors[$name]['messages'] : null; 
     181      $incidents = $vm->getFieldIncidents($name, AgaviValidator::NOTICE); 
     182      $msgs = array(); 
     183      foreach($incidents as $incident) { 
     184        foreach($incident->getErrors() as $error) { 
     185          $msgs[] = $error->getMessage(); 
     186        } 
     187      } 
     188      return $msgs; 
    167189    } else { 
    168190      $msgs = array(); 
    169191 
    170       foreach($errors as $errorName => $error) { 
    171         foreach($error['messages'] as $message) { 
    172           if(!isset($msgs[$message])) { 
    173             $msgs[$message] = array(); 
    174           } 
    175           $msgs[$message][] = $errorName; 
     192      $incidents = $vm->getIncidents(AgaviValidator::NOTICE); 
     193      $msgs = array(); 
     194      foreach($incidents as $incident) { 
     195        foreach($incident->getErrors() as $error) { 
     196          $msgs[] = array('message' => $error->getMessage(), 'errors' => $error->getFields()); 
    176197        } 
    177198      } 
    178  
    179       $retMsgs = array(); 
    180       $i = 0; 
    181       foreach($msgs as $message => $errorNames) { 
    182         $retMsgs[$i] = array('message' => $message, 'errors' => $errorNames); 
    183         ++$i; 
    184       } 
    185       return $retMsgs; 
     199      return $msgs; 
    186200    } 
    187201  } 
     
    214228  public function hasError($name) 
    215229  { 
    216     $errors = $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
    217     return isset($errors[$name]); 
     230    return $this->getContext()->getValidatorManager()->isFieldFailed($name); 
    218231  } 
    219232 
     
    230243  public function hasErrors() 
    231244  { 
    232     return (count($this->getAttribute('errors', 'org.agavi.validation.result', array())) > 0); 
     245    return $this->getContext()->getValidatorManager()->getResult() > AgaviValidator::NOTICE; 
    233246  } 
    234247 
     
    262275 
    263276  /** 
    264    * Remove an error. 
    265    * 
    266    * @param      string An error name. 
    267    * 
    268    * @return     string An error message, if the error was removed, otherwise 
    269    *                    null. 
    270    * 
    271    * @author     Sean Kerr <skerr@mojavi.org> 
    272    * @author     David Zuelke <dz@bitxtender.com> 
    273    * @author     Dominik del Bondio <ddb@bitxtender.com> 
    274    * @since      0.9.0 
    275    */ 
    276   public function removeError($name) 
    277   { 
    278     $errors =& $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
    279     $retval = null; 
    280  
    281     if(isset($errors[$name])) { 
    282       $retval = $errors[$name]; 
    283       unset($errors[$name]); 
    284     } 
    285  
    286     return $retval; 
    287   } 
    288  
    289   /** 
    290277   * Set an error. 
    291278   * 
     
    293280   * @param      string An error message. 
    294281   * 
    295    * @author     Sean Kerr <skerr@mojavi.org> 
    296282   * @author     Dominik del Bondio <ddb@bitxtender.com> 
    297283   * @since      0.9.0 
     
    299285  public function setError($name, $message) 
    300286  { 
    301     // set the attribute first if it doesn't exist, else we will not a proper  
    302     // reference to the attribute. 
    303     if(!$this->hasAttribute('errors', 'org.agavi.validation.result')) { 
    304       $this->setAttribute('errors', array(), 'org.agavi.validation.result'); 
    305     } 
    306     $errors =& $this->getAttribute('errors', 'org.agavi.validation.result'); 
    307     if(!isset($errors[$name])) { 
    308       $errors[$name] = array('messages' => array(), 'validators' => array()); 
    309     } 
    310     $errors[$name]['messages'][] = $message; 
     287    $vm = $this->getContext()->getValidatorManager(); 
     288    $incident = new AgaviValidationIncident(null, AgaviValidator::ERROR); 
     289    $incident->addError(new AgaviValidationError($message, null, array($name))); 
     290    $vm->addIncident($incident); 
    311291  } 
    312292 
     
    321301   *                   messages. 
    322302   * 
    323    * @author     Sean Kerr <skerr@mojavi.org> 
    324303   * @author     Dominik del Bondio <ddb@bitxtender.com> 
    325304   * @since      0.9.0 
     
    327306  public function setErrors(array $errors) 
    328307  { 
    329     // set the attribute first if it doesn't exist, else we will not a proper  
    330     // reference to the attribute. 
    331     if(!$this->hasAttribute('errors', 'org.agavi.validation.result')) { 
    332       $this->setAttribute('errors', array(), 'org.agavi.validation.result'); 
    333     } 
    334     $storedErrors =& $this->getAttribute('errors', 'org.agavi.validation.result', array()); 
     308    $vm = $this->getContext()->getValidatorManager(); 
     309    $incident = new AgaviValidationIncident(null, AgaviValidator::ERROR); 
    335310    foreach($errors as $name => $error) { 
    336       if(!isset($storedErrors[$name])) { 
    337         $storedErrors[$name] = array('messages' => array(), 'validators' => array()); 
    338       } 
    339       if(!is_array($error)) { 
    340         $storedErrors[$name]['messages'][] = $error; 
    341       } else { 
    342         $storedErrors[$name]['messages'] = array_merge($storedErrors[$name]['messages'], $error); 
    343       } 
    344     } 
     311      $incident->addError(new AgaviValidationError($error, null, array($name))); 
     312    } 
     313 
     314    $vm->addIncident($incident); 
    345315  } 
    346316 
  • branches/0.11/src/validator/AgaviBaseFileValidator.class.php

    r1430 r1444  
    4141abstract class AgaviBaseFileValidator extends AgaviValidator 
    4242{ 
     43 
    4344  /** 
    44    * Returns whether all arguments are files in the request. 
     45   * Returns whether all arguments are set in the validation input parameters. 
     46   * Set means anything but empty string. 
     47   * 
     48   * @param      bool Whether an error should be thrown for each missing  
     49   *                  argument if this validator is required. 
    4550   * 
    4651   * @return     bool Whether the arguments are set. 
     
    4954   * @since      0.11.0 
    5055   */ 
    51   protected function hasAllArgumentsSet() 
     56  protected function checkAllArgumentsSet($throwError = true) 
    5257  { 
    5358    $request = $this->getContext()->getRequest(); 
     59 
     60    $isRequired = $this->getParameter('required', true); 
     61    $result = true; 
     62 
     63    $array = $this->validationParameters->getParameters(); 
     64    $baseParts = $this->curBase->getParts(); 
    5465    foreach($this->getArguments() as $argument) { 
    5566      $new = $this->curBase->pushRetNew($argument); 
    5667      $pName = $this->curBase->pushRetNew($argument)->__toString(); 
    5768      if(!$request->hasFile($pName)) { 
    58         return false; 
     69        if($throwError && $isRequired) { 
     70          $this->throwError(null, $pName); 
     71        } 
     72        $result = false; 
    5973      } 
    6074    } 
    61     return true; 
     75    return $result; 
    6276  } 
    63  
    6477 
    6578  /** 
  • branches/0.11/src/validator/AgaviIValidatorContainer.interface.php

    r1245 r1444  
    4040   
    4141  /** 
    42    * Fetches the request. 
    43    *  
    44    * @return     AgaviRequest The request to be used by child validators. 
    45    *  
    46    * @author     Uwe Mesecke <uwe@mesecke.net> 
    47    * @since      0.11.0 
    48    */ 
    49   public function getRequest(); 
    50    
    51   /** 
    5242   * Fetches the dependency manager 
    5343   *  
     
    6050  public function getDependencyManager(); 
    6151 
    62   /** 
    63    * Reports an error to the parent container. 
    64    *  
    65    * @param      AgaviValidator The validator where the error occured. 
    66    * @param      string         An error message. 
    67    *  
    68    * @author     Dominik del Bondio <ddb@bitxtender.com> 
    69    * @since      0.11.0 
    70    */ 
    71   public function reportError(AgaviValidator $validator, $errorMsg); 
    7252} 
    7353?> 
  • branches/0.11/src/validator/AgaviIssetValidator.class.php

    r1245 r1444  
    3232{ 
    3333  /** 
     34   * We return true here no matter what because we will check for existance 
     35   * ourself. 
     36   * 
     37   * @param      bool Whether an error should be thrown for each missing  
     38   *                  argument if this validator is required. 
     39   * 
     40   * @return     bool Whether the arguments are set. 
     41   * 
     42   * @author     Dominik del Bondio <ddb@bitxtender.com> 
     43   * @since      0.11.0 
     44   */ 
     45  protected function checkAllArgumentsSet($throwError = true) 
     46  { 
     47    return true; 
     48  } 
     49 
     50  /** 
    3451   * Validates the input. 
    3552   *  
     
    4158  protected function validate() 
    4259  { 
     60    $params = $this->validationParameters->getParameters(); 
     61 
    4362    foreach($this->getArguments() as $argument) { 
    44       if(!$this->parentContainer->getRequest()->hasParameter($argument)) { 
     63      if(!$this->curBase->hasValueByChildPath($argument, $params)) { 
    4564        $this->throwError(); 
    4665        return false; 
  • branches/0.11/src/validator/AgaviOperatorValidator.class.php

    r1413 r1444  
    5555   * @since      0.11.0 
    5656   */ 
    57   public function __construct(AgaviIValidatorContainer $parent, array $arguments, array $errors = array(), array $parameters = array()) 
     57  public function __construct(AgaviIValidatorContainer $parent, array $arguments, array $errors = array(), array $parameters = array(), $name = '') 
    5858  { 
    59     parent::__construct($parent, $arguments, $errors, $parameters); 
     59    parent::__construct($parent, $arguments, $errors, $parameters, $name); 
    6060     
    6161    if($this->getParameter('skip_errors')) { 
     
    101101   
    102102  /** 
    103    * Submits an error to the error manager. 
    104    *  
    105    * The stuff in the parameter specified in $index is submitted to the 
    106    * error manager. If there is no parameter with this name, then 'error' 
    107    * is tryed as an parameter and if even this fails, the stuff in 
    108    * $backupError is sent. 
    109    *  
    110    * @param      string The name of the error parameter to fetch the message  
    111    *                    from. 
    112    * @param      string An default error message to be used if the given error  
    113    *                    has no message set. 
    114    * 
    115    * @author     Uwe Mesecke <uwe@mesecke.net> 
    116    * @since      0.11.0 
    117    */ 
    118   protected function throwError($index = null, $backupError = null) 
    119   { 
    120     $error = $this->getErrorMessage($index, $backupError); 
    121  
    122     // if no error msg was supplied rethrow the child errors 
    123     if($error === null) { 
    124       foreach($this->errors as $childError) { 
    125         $this->parentContainer->reportError($childError[0], $childError[1]); 
    126       } 
    127     } else { 
    128       if($this->hasParameter('translation_domain')) { 
    129         $error = $this->getContext()->getTranslationManager()->_($error, $this->getParameter('translation_domain')); 
    130       } 
    131  
    132       $this->parentContainer->reportError($this, $error); 
    133     } 
    134   } 
    135  
    136   /** 
    137    * Reports an error to the parent container. 
    138    *  
    139    * @param      AgaviValidator The validator where the error occured. 
    140    * @param      string         An error message. 
    141    *  
    142    * @author     Dominik del Bondio <ddb@bitxtender.com> 
    143    * @since      0.11.0 
    144    * @see        AgaviIValidatorContainer::reportError 
    145    */ 
    146   public function reportError(AgaviValidator $validator, $errorMsg) 
    147   { 
    148     $this->errors[] = array($validator, $errorMsg); 
    149   } 
    150    
    151   /** 
    152103   * Adds new child validator. 
    153104   *  
     
    175126      $this->addChild($validator); 
    176127    } 
    177   } 
    178    
    179   /** 
    180    * Gets the request from the parent. 
    181    *  
    182    * @return     AgaviRequest The parent's request. 
    183    * 
    184    * @author     Uwe Mesecke <uwe@mesecke.net> 
    185    * @since      0.11.0 
    186    */ 
    187   public function getRequest() 
    188   { 
    189     return $this->parentContainer->getRequest(); 
    190128  } 
    191129   
  • branches/0.11/src/validator/AgaviValidator.class.php

    r1422 r1444  
    4343{ 
    4444  /** 
     45   * validator field success flag 
     46   */ 
     47  const NOT_PROCESSED = -1; 
     48 
     49  /** 
    4550   * validator error severity (the validator succeeded) 
    4651   */ 
     
    107112 
    108113  /** 
    109    * @var        array The field names which have been validated by this 
    110    *                   validator 
    111    */ 
    112   protected $validatedFieldnames = array(); 
    113  
    114   /** 
    115114   * @var        array The name of the request parameters serving as argument to 
    116115   *                   this validator. 
     
    124123 
    125124  /** 
    126    * @var        string The last error index. When the validator throws an error 
    127    *                    the index which was thrown is stored here. 
    128    */ 
    129   protected $lastErrorIndex = null; 
     125   * @var        AgaviValidationIncident The current incident. 
     126   */ 
     127  protected $incident = null; 
    130128 
    131129  /** 
     
    382380   * Set means anything but empty string. 
    383381   * 
     382   * @param      bool Whether an error should be thrown for each missing  
     383   *                  argument if this validator is required. 
     384   * 
    384385   * @return     bool Whether the arguments are set. 
    385386   * 
     
    387388   * @since      0.11.0 
    388389   */ 
    389   protected function hasAllArgumentsSet() 
    390   { 
     390  protected function checkAllArgumentsSet($throwError = true) 
     391  { 
     392    $isRequired = $this->getParameter('required', true); 
     393    $result = true; 
     394 
    391395    $array = $this->validationParameters->getParameters(); 
    392396    $baseParts = $this->curBase->getParts(); 
     
    395399      $pName = $this->curBase->pushRetNew($argument)->__toString(); 
    396400      if(!$this->validationParameters->hasParameter($pName) || $this->validationParameters->getParameter($pName) === "") { 
    397         return false; 
    398       } 
    399     } 
    400     return true; 
     401        if($throwError && $isRequired) { 
     402          $this->throwError(null, $pName); 
     403        } 
     404        $result = false; 
     405      } 
     406    } 
     407    return $result; 
    401408  } 
    402409 
     
    432439   * Submits an error to the error manager. 
    433440   * 
    434    * The stuff in the parameter specified in $index is submitted to the 
    435    * error manager. If there is no parameter with this name, then 'error' 
    436    * is tryed as an parameter and if even this fails, the stuff in 
    437    * $backupError is sent. 
     441   * Will look up the index in the errors array with automatic fallback to the 
     442   * default error. You can optionally specify the fields affected by this  
     443   * error. The error will be appended to the current incident. 
    438444   * 
    439445   * @param      string The name of the error parameter to fetch the message  
    440446   *                    from. 
    441    * @param      string An default error message to be used if the given error  
    442    *                    has no message set. 
    443    * 
    444    * @author     Uwe Mesecke <uwe@mesecke.net> 
    445    * @since      0.11.0 
    446    */ 
    447   protected function throwError($index = null, $backupError = null) 
    448   { 
    449     $this->lastErrorIndex = $index; 
    450  
    451     $error = $this->getErrorMessage($index, $backupError); 
     447   * @param      string|array The arguments which are affected by this error. 
     448   *                          If null is given it will affect all fields 
     449   * 
     450   * @author     Dominik del Bondio <ddb@bitxtender.com> 
     451   * @since      0.11.0 
     452   */ 
     453  protected function throwError($index = null, $affectedArgument = null) 
     454  { 
     455    if($affectedArgument === null) { 
     456      $affectedArguments = $this->getFullArgumentNames(); 
     457    } else { 
     458      $affectedArguments = (array) $affectedArgument; 
     459    } 
     460 
     461    $error = $this->getErrorMessage($index); 
    452462 
    453463    if($this->hasParameter('translation_domain')) { 
     
    455465    } 
    456466 
    457     $this->reportError($this, $error); 
    458   } 
    459  
    460  
    461   /** 
    462    * Reports an error to the parent container. 
    463    * 
    464    * @param      AgaviValidator The validator where the error occured. 
    465    * @param      string         An error message. 
    466    * 
    467    * @author     Dominik del Bondio <ddb@bitxtender.com> 
    468    * @since      0.11.0 
    469    * @see        AgaviIValidatorContainer::reportError 
    470    */ 
    471   public function reportError(AgaviValidator $validator, $errorMsg) 
    472   { 
    473     if(self::mapErrorCode($this->getParameter('severity', 'error')) > self::NONE) { 
    474       $this->parentContainer->reportError($validator, $errorMsg); 
    475     } 
    476   } 
    477  
    478   /** 
    479    * Returns a list of input fields that are per default affected by a failure 
    480    * of the validator 
    481    * 
    482    * The list consists of the fields in the parameters that are lists in 
    483    * affectedFieldNames and the space seperated list of fields in the 
    484    * parameter 'affects'. 
    485    * 
    486    * @return     array The list of fields that are affected by an error. 
    487    * 
    488    * @author     Uwe Mesecke <uwe@mesecke.net> 
    489    * @since      0.11.0 
    490    */ 
    491   public function getAffectedFields() 
    492   { 
    493     $fields = array(); 
    494     $base = $this->curBase->__toString(); 
    495  
    496     if($this->hasParameter('affects')) { 
    497       $f = array_map('trim', explode(' ', trim($this->getParameter('affects')))); 
    498       foreach($f as $n) { 
    499         if(!strlen($n)) { 
    500           continue; 
    501         } 
    502         $fields[] = $n; 
    503       } 
    504     } 
    505  
    506     $fields = array_merge($fields, $this->validatedFieldnames); 
    507     // filter out empty strings 
    508     $fields = array_filter($fields, 'strlen'); 
    509  
    510     return array_unique($fields); 
    511   } 
     467    if(!$this->incident) { 
     468      $this->incident = new AgaviValidationIncident($this, self::mapErrorCode($this->getParameter('severity', 'error'))); 
     469    } 
     470 
     471    $this->incident->addError(new AgaviValidationError($error, $index, $affectedArguments)); 
     472  } 
     473 
    512474 
    513475  /** 
     
    560522      } 
    561523 
    562       $fieldNames = array(); 
    563       foreach($this->getArguments() as $argument) { 
    564         $name = $this->curBase->pushRetNew($argument)->__toString(); 
    565         $fieldNames[] = $name; 
    566         $this->validatedFieldnames[] = $name; 
    567       } 
     524      $fieldnames = $this->getFullArgumentNames(); 
    568525 
    569526      $result = self::SUCCESS; 
    570527      $errorCode = self::mapErrorCode($this->getParameter('severity', 'error')); 
    571528 
    572       if($this->hasAllArgumentsSet()) { 
     529      if($this->checkAllArgumentsSet(false)) { 
    573530        if(!$this->validate()) { 
    574531          // validation failed, exit with configured error code 
     
    580537          $result = $errorCode; 
    581538        } else { 
    582           // no reason to throw any error since it wouldn't be included anyways 
    583           $result = self::NONE; 
     539          // we don't throw an error here because this is not an incident per se 
     540          // but rather a non validated field 
     541          $result = self::NOT_PROCESSED; 
    584542        } 
    585543      } 
    586544 
    587545      $vm = $this->getContext()->getValidatorManager(); 
    588       foreach($fieldNames as $fieldName) { 
    589         $vm->addFieldResult($this, $fieldName, $result, $this->lastErrorIndex); 
     546      foreach($fieldnames as $fieldname) { 
     547        $vm->addFieldResult($this, $fieldname, $result); 
     548      } 
     549 
     550      if($this->incident) { 
     551        $vm->addIncident($this->incident); 
     552        $this->incident = null; 
    590553      } 
    591554 
     
    722685  } 
    723686 
     687  /** 
     688   * Returns all arguments with their full path. 
     689   * 
     690