#1 2011-02-16 18:13:44

migajek
Member
Registered: 2010-10-01
Posts: 89

Record Validation?

Hi,
let's say I have a simple record adding procedure, which creates record and fills it with a data provided by user (possibly using SQLite3UIEdit). What is the proper approach for validating data (like, checking if the string is not too short, or - as in my case - validate the provided string against regexp).
thanks,
migajek

Offline

#2 2011-02-16 19:05:39

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Record Validation?

Good point.
There is no direct way of doing this.

I've therefore added this kind of event handler to the form:

type
  /// Event used for individual field validation
  // - must return TRUE if the specified field is correct, FALSE if the content
  // is to be modified
  // - it's up to the handler to inform the user that this field is not correct,
  // via a popup message for instance
  TOnComponentValidate = function(EditControl: TControl; Prop: PPropInfo): boolean;

  TRecordEditForm = class(TRTTIForm)
    (...)
    /// event called to check if the content of a field on form is correct
    // - is checked when the user press the "Save" Button
    // - if returns false, component is focused and window is not closed
    property OnComponentValidate: TOnComponentValidate read fOnComponentValidate write fOnComponentValidate;

See http://synopse.info/fossil/info/3492970fc3

Offline

#3 2011-02-16 19:37:34

migajek
Member
Registered: 2010-10-01
Posts: 89

Re: Record Validation?

that event handler is pretty good idea for a quick fix, but doesn't solve the problem in my opinion.

first of all, I think it'd be good to keep the logics separated from presentation layer, so the validation part should be done on the Record, not the editor.
Let's say I need to store IP address in a record, and I have pretty much ways of adding/editing record (for example, creating the new record directly or modifying it's data inside of a grid). No matter what "editor" I use, the "IP" fields still needs to be valid IP address.

From what I learned using PHP frameworks, automated data validation is very important and convenient. My suggestion is to expand TSQLRecord with methods like this:

//pseudo code

// run the validation of data
// implemented in TSQLRecord, 
// calls the GetValidationRules and check data against rules one-by-one.
// Validate method is called each time the object is about to be saved
// or is edited
function Validate(): boolean; 

// returns list of rules
// actually implemented by final (inheriting) class.
// each validation rule consists of 
// * field name to which rule applies, 
// * rule name or even better - rule class
// * rule additional parameters (string - JSON-formated? array of const? I dunno ...)
function GetValidationRules: TValidationRules;

// registers a callback to be called on validation error
procedure RegisterValidationCallback(CB: TValidationCallback);

// Field: field name 
// Message: message returned by validation rule
// FieldValue: proposed field value / field value after filtering
// AllowContinue: if set to true, it means there was no error in fact, the validator acts as a "filter" here (see below)
TValidationCallback = procedure (const AField: string; AMessage: string; AFieldValue: string; AAllowContinue: boolean)



example filters:
TRegexpRule:
 * checks given value against regexp provided as parameter. if it doesn't pass, do callback, with message "sorry it doesn't validate against regexp"

TToLowercaseFilter:
 * calls Callback with AAllowContinue = true and  AField = lowercase(OriginalFieldValue)
 this doesn't throw an actual error but ensures the field will be lowercase.

actually it might be wise to separate Rules from Filters, or give them separate callbacks.


implementation of GetValidationRules for my case would return array looking like this:

[['Email', TEmailRule, 'allowedDomains:.com;.pl;.eu;.us'],
['Userip', TIPRule],
['Userlogin', TLowercaseFilter],
['Userlogin', TStringLengthRule, 'minLength:5|maxLength:25']]

that is just an idea, I know many things might be hard to implement in delphi, they might not be optimal etc but hopefully I know what I have in my mind.

That way one would be able to simply define filters, expand filtering (creating own filters), handle errors (for example, when creating custom "editor" for the record, just registerValidationCallback and add labels containing errors for each field).



What do you think about it?

Offline

#4 2011-02-16 19:56:09

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Record Validation?

Nice point.

I'll add such a feature to my to-do list...
It could be made easy by using some callbacks exposed via the RTTI (in a similar way of how RESTful Services are implemented)...

Then those validation rules will be available as standard in the SQLite3UIEdit unit, without no external event.

Offline

#5 2011-02-16 20:07:19

migajek
Member
Registered: 2010-10-01
Posts: 89

Re: Record Validation?

I don't know "RTTI callbacks" so I'll have to investigate them a bit wink
But anyway it might be convenient to expose them somehow, in case I want to do validation on my own (in example - creating own editor for a model) so that I can handle validation messages.

Offline

#6 2011-02-16 20:20:08

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Record Validation?

It's a nice idea having validation message handled in the event handlers..

So you can use the exact same code for a rich Client or a HTML page...

I've printed your proposal and will try to find the best way of adding this feature to the framework!

Offline

#7 2011-02-16 20:56:01

migajek
Member
Registered: 2010-10-01
Posts: 89

Re: Record Validation?

so you can use the exact same code for a rich Client or a HTML page...

I don't get that, probably because I have absolutely no experience with web services in Delphi. Not even 'hello world' wink

ok, I'm looking forward to hear your proposals tongue

Last edited by migajek (2011-02-16 20:56:51)

Offline

#8 2011-02-21 22:18:56

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Record Validation?

I've added the first draft of auto-filtering in the framework.

See http://synopse.info/fossil/info/85dfa822ed
- introducing direct content filtering and validation using TSQLFilterOrValidator dedicated classes
-filtering is handled directly in the new TSQLRecord.Filter virtual method, or via some TSQLFilter classes - TSQLFilterUpperCase, TSQLFilterUpperCaseU, TSQLFilterLowerCase, TSQLFilterLowerCaseU and TSQLFilterTrim e.g.

To initialize it, you can add some filters to your TSQLModel creation function:

function CreateFileModel(Owner: TSQLRest): TSQLModel;
var Classes: array[0..high(FileTabs)] of TSQLRecordClass;
    i: integer;
begin
  for i := 0 to high(FileTabs) do
    Classes[i] := FileTabs[i].Table;
  result := TSQLModel.Create(Classes,'synfile');
  result.Owner := Owner;
  result.SetActions(TypeInfo(TFileAction));
  result.SetEvents(TypeInfo(TFileEvent));
  TSQLFile.RecordProps.AddFilterOrValidator('Name',TSQLFilterLowerCase);
end;

I've modified SQLite3UIEdit so that it takes in account this filters.

Next step is the validation, but you've got the scheme!

Offline

#9 2011-02-22 21:17:30

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Record Validation?

Here we are.

I've added validation in the SQLite3Commons unit.
It's now totally independent from the UI part of the framework.

See http://synopse.info/fossil/info/5c03626961
and mostly http://synopse.info/fossil/info/14785e263f

TSQLRecord field content validation is handled in the new TSQLRecord.Validate virtual method, or via some TSQLValidate classes - see TSQLValidateRest, TSQLValidateIPAddress, TSQLValidateEmail, TSQLValidatePattern, TSQLValidatePatternI, TSQLValidateText, TSQLValidatePassWord e.g.

You have powerful validation classes for IP Address, Email (with TLD+domain name), simple regex pattern, textual validation, strong password validation...

SQLite3UIEdit unit now now handle TSQLRecord automated filtering (using TSQLFilter classes) and validation (using TSQLValidate classes).
The unique field validation is now in TSQLRecord.Validate and not in SQLite3UIEdit itself (better multi-tier architecture).

Feedback is welcome!
see http://synopse.info/forum/viewtopic.php?pid=1443

Offline

#10 2011-02-22 21:19:51

migajek
Member
Registered: 2010-10-01
Posts: 89

Re: Record Validation?

Thank you smile I'll update the source and dig into it tomorrow smile

Last edited by migajek (2011-02-22 21:20:03)

Offline

Board footer

Powered by FluxBB