We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 715
    • 15 Posts
    Where can I find some sample code or documentation about using xPDOObject validation. i.e. validation usage and rule creation/generation

    Thanks in advance.
    • Quote from: airoctive at Dec 22, 2008, 08:08 PM

      Where can I find some sample code or documentation about using xPDOObject validation. i.e. validation usage and rule creation/generation
      Documentation efforts are in progress, but here’s some basic information...

      Schema-defined validation rules
      You can, at build time, have validation rules defined in your xPDO schema which are generated into the object map file. Here is an example from the sample model available in SVN:
              <validation class="validation.xPDOValidator">
                  <rule field="dob" name="date_format" type="preg_match" rule="/\d{4}-\d{2}-\d{2}/" />
                  <rule field="password" name="password_length" type="xPDOValidationRule" rule="xPDOMinLengthValidationRule" value="6" />
              </validation>


      Programmatic validation rules
      You can also add validation rules via the API. Here’s an example using the modX class, which extends xPDO:
      <?php
      $object = $modx->newObject('modDocument');
      $object->set('alias', '1');
      
      $object->addValidationRule('alias', 'alias_match', 'preg_match', '/^123/', array('message' => 'Invalid alias'));
      $object->addValidationRule('alias', 'alias_length', 'xPDOValidationRule', 'xPDOMinLengthValidationRule', array('value' => 2, 'message' => 'Alias too short'));
      
      $object->validate();
      ?>


      Manual validation vs. validation on save
      Regardless of whether you have pre-defined rules in your object, or have added them dynamically at run-time in a script, you can then use manual validation and/or auto-validation that occurs on save. Using our example programmatic rules from above, you can now simply do the following to find out if it’s valid:
      <?php
      $valid = $object->validate();
      if (!$valid) {
          // handle the exception
      }
      ?>

      Additionally, you can inspect the results of validation and/or messages generated by the process easily by using the object’s validator:
      <?php
      $validator = $object->getValidator();
      if (!$object->validate()) {
          $results = $validator->getResults();
          $messages = $validator->getMessages();
          // do what you want with the specific results and/or messages validate() generated...
      }
      ?>


      Also, xPDO allows you to control validation when objects are saved via the XPDO_OPT_VALIDATE_ON_SAVE option. If this option is passed as true as in the following example, then validate() will be run on the object when save() is called and if rules fail, save will not continue:
      <?php
                  $myModel = new xPDO(
                      $database_dsn,
                      $database_user,
                      $database_password,
                      array (
                          XPDO_OPT_CACHE_PATH => '/path/to/cache/dir/',
                          XPDO_OPT_TABLE_PREFIX => 'mymodel_',
                          XPDO_OPT_HYDRATE_FIELDS => true,
                          XPDO_OPT_HYDRATE_RELATED_OBJECTS => true,
                          XPDO_OPT_HYDRATE_ADHOC_FIELDS => true,
                          XPDO_OPT_VALIDATE_ON_SAVE => true,
                      ),
                      array (
                          PDO_ATTR_ERRMODE => PDO_ERRMODE_SILENT,
                          PDO_ATTR_PERSISTENT => false,
                          PDO_MYSQL_ATTR_USE_BUFFERED_QUERY => true
                      )
                  );
                  $this->setPackage('mymodel', '/path/to/mymodel/root/dir/');
      ?>

      You can also override the xPDOValidator class, but I’ll save that for the documentation. You can see the modValidator class for an example though.

      Validation rule types
      Also, validation rules can be one of three basic types:

      • callable - Any callable PHP function.
      • preg_match - Define a simple preg_match pattern.
      • xPDOValidationRule - Use the pre-defined derivatives or extend xPDOValidationRule yourself to create custom rule classes which handle their own parameters and can easily interact with the xPDOValidator class that is responsible for executing the validation process.
        • 715
        • 15 Posts
        Thanks a bunch, information very useful as always. Thanks Jason.
          • 26471
          • 17 Posts
          I have an xPDOObject instance fetched like this:

          $xpdo = new xPDO(...
          $targetPK = array('pk1' => $pk1, 'pk2' => $pk2, 'pk3' => $pk3);
          $myObject = $xpdo->getObject('myObject', $targetPK);
          


          The object comes back fine and has the expected field values when I check my row data with $myObject->get(’field’).

          I then go on to dynamically add a validation rule for ’pk1’:

          $myObject->addValidationRule('pk1', 'pk1_format', 'preg_match', '/regex_that_fails/', array('message' => 'PK1 validation failed!'));
          


          The validation ought to fail, but succeeds:

          if (! $myObject->validate()) {
             print 'Validation failed! ' . implode(';', $myObject->getValidator()->getMessages());
          } else {
             print 'Validation succeeded!'; // this is what I get
          }
          


          Furthermore:

          assert( $myObject->getValidator() ); // assertion success, presumably validator object is fine?
          assert( $myObject->isValidated() ); // assertion failure
          


          When I enable xPDO debugging, I see a warning message "Validation succeeded".

          Any idea as to why the validate() call succeeds (when it shouldn’t) and subsequently isValidated() returns false? Does validation not work on fetched objects or is something else going on?

          Cheers

          EDIT: I should mention, using SVN rev 329 (currently 333 but AFAIK no updates in that time that may affect validation)
          • SD:

            I would expect it to work exactly as you do. Is it possible this is a bug with only having a single validation rule? Also, can you share your PHP version information?
              • 26471
              • 17 Posts
              Thanks for the response! I’m using PHP 5.1.6 with MySQL 5.0.45 on a CentOS 5 x86 server.

              I have added 3 additional validation rules (including non-PK fields) with no change to the situation. Regular expressions that ought to fail are passing. I thought that one possible explanation for this is that rule/s are not being applied - so out of curiosity, I modified one of my rules:

              // The rule type "Zpreg_match" should definitely cause a failure..
              $myObject->addValidationRule('field1', 'field1_format', 'Zpreg_match', '/^[1-9]$/', 
                 array('message' => 'expecting an integer between 1 and 9.', 'stopOnRuleFail' => true));
              


              I’d expect to see a debug message "Unsupported validation rule" out of xPDOValidator::validate() - but this error message is not seen. So validate() does not proceed into the "body" of the validation logic. One possible early exit point in the validate() method would be in the case of there being no validation rules - but this cannot be the case since I’m getting a "Validation succeeded" and there is no message to the effect of "Validation called but no rules were found."

              There are no additional PHP errors or warnings in server logs.

              I cannot proceed with my project without solving this, so I will continue to debug and inspect the xPDO validation internals to see what I find..
                • 26471
                • 17 Posts
                After adding additional debug output in xPDOValidator::validate() and xPDOObject::isValidated(), I think I’ve gotten to the source of the problem.

                ALL validation is skipped in the case where the given column does not exist in the "$_dirty" list - so as it currently stands, validation is not possible for columns that have not been modified by a call to $myObject->set(’field’, $value).  Fetched objects cannot be validated unless their fields are modified first.

                Also, a call to $myObject->validate() just after object retrieval returns "true" since the return from myObject::isValidated() returns true as both the "$_dirty" and "$_validated" arrays are empty (and deemed to be unchanged) - even though no validation has taken place.

                Would it be possible to implement an additional flag, and update the validation logic accordingly to accommodate validation of fetched objects?  I know that technically all fields should be valid (since they are already in the database!) but I think this behaviour would be useful in those situations where the DB schema is updated and you want to verify, programatically, that your existing data still conforms to the expected field constraints.


                EDIT: I’m now using the workaround: $myObject->setDirty() after fetching, which solves my problem temporarily. I can’t help but feel though that there should be a more elegant approach to this.. either a global flag like ’XPDO_OPT_VALIDATE_IGNORE_DIRTY’ or, the ability to force validation regardless of "dirty" status on a per-object basis e.g. as part of the params passed to $myObject->addValidationRule() which could look like:

                $myObject->addValidationRule('field1', 'field1_format', 'preg_match', '/regex/', array(
                   'message' => 'Field1 validation failed!',
                   'forceValidation' => true
                ));
                


                I’m sure there could be other ways to implement this.. if, of course it fits with the architecture / design of xPDO. I’ll leave it up to the devs, just putting my thoughts out there!
                • Hmm, I think that works exactly as expected.  I’ll consider your thoughts on allowing validation of non-dirty data, but if you want to validate already saved data, setting it dirty is currently the way to go.  Or you can create a new object and copy the values into it using toArray() and fromArray().