#1 2012-04-04 10:35:27

coblongpamor
Member
From: Bali-Indonesia
Registered: 2010-11-07
Posts: 130
Website

How to use TSynFilterOrValidate

i just modified CreateFileModel function on the MainDemo sample project.
adding two lines of code to implement TSynFilterOrValidate as stated on the documentation.

...
TSQLMemo.AddFilterOrValidate('Name',TSynValidateUniqueField.Create());
TSQLMemo.AddFilterOrValidate('Name',TSynFilterUpperCase.Create());
end;

but it is no effect. SQLMemo still accept two or more duplicates name, and also with the uppercase issue.
what is wrong, with piece of code above?

thank you.
regard.

Offline

#2 2012-04-04 12:00:27

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

Re: How to use TSynFilterOrValidate

Filters have to be checked at runtime.

This is to be done by the UI or in your code.

See for instance how SQLite3UIEdit works:

procedure TRecordEditForm.BtnSaveClick(Sender: TObject);
 (...)

  // perform all registered filtering
  Rec.Filter(ModifiedFields);
  // perform content validation
  FieldIndex := -1;
  ErrMsg := Rec.Validate(Client,ModifiedFields,@FieldIndex);
  if ErrMsg<>'' then begin
    // invalid field content -> show message, focus component and abort saving
    if cardinal(FieldIndex)<cardinal(length(fFieldComponents)) then begin
      C := fFieldComponents[FieldIndex];
      C.SetFocus;
      Application.ProcessMessages;
      ShowMessage(ErrMsg,format(sInvalidFieldN,[fFieldCaption[FieldIndex]]),true);
    end else
      ShowMessage(ErrMsg,format(sInvalidFieldN,['?']),true);
  end else
    // close window on success
    ModalResult := mrOk;
end;

I've committed some documentation enhancement about filtering and validating records.
See http://synopse.info/fossil/info/6d31a4de6f

Offline

#3 2012-04-04 14:28:20

coblongpamor
Member
From: Bali-Indonesia
Registered: 2010-11-07
Posts: 130
Website

Re: How to use TSynFilterOrValidate

1. I've tried to implement a filter() features at TSQLFile like this.

procedure TEditForm.BtnOkClick(Sender: TObject);
var
  P: TSQLRecordProperties;
begin
  if ReadOnly then
    ModalResult := mrCancel else begin
    Rec.fModified := Iso8601Now;
    Rec.fName := trim(S2U(Name.Text));
    P:= Rec.RecordProps;
    Rec.Filter();
    //Rec.Validate()
....

but it does not work as expected.
2. because TSQLRecord.Validate () requires a parameter TSQLRest, while no declaration TSQLRest in this form, then for a while I did not apply TSQLRecord.Validate here. (Actually I was hoping to get property of TSQLRecordProperties TSQLRest, but it was not, I just found a property TSQLModel).

because no.1 does not work as I expect, and now I do not want to add parameters to

TEditForm.SetRec TSQLRest (const Value: TSQLFile)

procedure, so I tried to apply it to the exclusion of such ComputeFieldsBeforeWrite method.

procedure TSQLFile.ComputeFieldsBeforeWrite(aRest: TSQLRest; aOccasion: TSQLEvent);
var F: integer;
begin
  inherited;
  if (self<>nil) and (aRest<>nil) then
    with RecordProps do begin
      //for F:= 0 to High(Fields) do
        //if Fields[F].ShortName= 'Name' then
          //SetStrProp(Self,Fields[F].ShortName,Name+IntToStr(F));
        TSQLRecord(Self).Filter();
        TSQLRecord(Self).Validate(aRest);
    end;
end;

but it also does not work.
Can you give me some advice?

also, is it possible to get TSQLRest from TSQLRecordProperties property? or have to passing parameter as SQLite3UIEdit does?

Offline

#4 2012-04-04 14:54:04

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

Re: How to use TSynFilterOrValidate

TSQLRecordProperties is at record level, about RTTI.
The same TSQLRecordProperties can be shared among several TSQLRest instance, so it is not possible to get TSQLRest from TSQLRecordProperties property.

In fact, you can just pass nil to Validate() as aRest parameter, and it will work for most validators.
Validate() method only expect aRest to be available for TSynValidateRest kind of validators.
It is just non used for other kind of validators, like TSynFilterUpperCase.
But if you want TSynValidateUniqueField validators, you'll need to specify a aRest parameter.

Using a ComputeFieldsBeforeWrite overidden method won't be good either, since it is not about validation, but filtering.
You can run Filter() here, but it is not the right place to use Validate().

What do you want to implement?
Validate the content when adding or updating a record?
In case of wrong validation, do you expect an exception to be raised, for instance?
I guess this is to work on client side.

Up to now, the filtering is not implemented at CRUD level, but at customer code level (UI, business logic code).
This does make sense in a n-Tier architecture.
Adding filtering and validation at CRUD level will mix DB and Business Logic layers.

Offline

#5 2012-04-04 16:24:49

coblongpamor
Member
From: Bali-Indonesia
Registered: 2010-11-07
Posts: 130
Website

Re: How to use TSynFilterOrValidate

What do you want to implement?

with

TSQLFile.AddFilterOrValidate('Name',TSynFilterUpperCase.Create()); 

i want to update automatically lowercase user input of Name property to uppercase.
with

TSQLFile.AddFilterOrValidate('Name',TSynValidateUniqueField.Create());

i want to ensure that no Name value of TSQLFile record duplicated.

Validate the content when adding or updating a record?

Both. adding and updating.
TSynFilterUpperCase and TSynValidateUniqueField should be done when adding record.
when user try to modify the Name value of any record to something that are allready exists, TSynValidateUniqueField should reaise an exception.

In case of wrong validation, do you expect an exception to be raised, for instance?

Yes. like what SQLite3UIEdit does.

ErrMsg := Rec.Validate(Client,ModifiedFields,@FieldIndex);
  if ErrMsg<>'' then begin
    // invalid field content -> show message, focus component and abort saving
    if cardinal(FieldIndex)<cardinal(length(fFieldComponents)) then begin
      C := fFieldComponents[FieldIndex];
      C.SetFocus;
      Application.ProcessMessages;
      ShowMessage(ErrMsg,format(sInvalidFieldN,[fFieldCaption[FieldIndex]]),true);
    end else
      ShowMessage(ErrMsg,format(sInvalidFieldN,['?']),true);
  end

what i did is:
1.registering TSynFileteOrValiedate after TModel creation.

...
result.SetEvents(TypeInfo(TFileEvent));
  TSQLFile.AddFilterOrValidate('Name',TSynValidateUniqueField.Create());
  TSQLFile.AddFilterOrValidate('Name',TSynFilterUpperCase.Create());
end;

2. modifying SetRec function

function TEditForm.SetRec(const aClient: TSQLRestClient; const Value: TSQLFile): boolean;
begin
  fClient:= aClient;
  ...

3. performing registed filter and validation

procedure TEditForm.BtnOkClick(Sender: TObject);
begin
  if ReadOnly then
    ModalResult := mrCancel else begin
    Rec.fModified := Iso8601Now;
    Rec.fName := trim(S2U(Name.Text));

    Rec.Filter();
    Rec.Validate(fClient);
    ....

but still not work.

Offline

#6 2012-04-04 17:05:18

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

Re: How to use TSynFilterOrValidate

Rec.Validate returns a string which is not void on any error: it is an error message in this case.
You have to raise the exception by hand.

See TRecordEditForm.BtnSaveClick

Offline

#7 2012-04-05 03:41:03

coblongpamor
Member
From: Bali-Indonesia
Registered: 2010-11-07
Posts: 130
Website

Re: How to use TSynFilterOrValidate

ok. i think now i understand how to use TSynValidate, especially TSynValidateUniqueField.
but not for the TSynFilter, especially in this case TSynFilterUppercase.
how exactly to use TSynFilterUppercase?

thank you.
regard.

Offline

#8 2012-04-05 05:58:54

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

Re: How to use TSynFilterOrValidate

I think the documentation is clear about this point.

    {{ filter the specified fields values of the current TSQLRecord instance
    - by default, this will perform all TSynFilter as registered by
      [RecordProps.]AddFilterOrValidate()
    - inherited classes may add some custom filtering here, if it's not needed
      nor mandatory to create a new TSynFilter class type: in this case, the
      function has to return TRUE if the filtering took place, and FALSE if
      any default registered TSynFilter must be processed
    - the default aFields parameter will process all fields }
    function Filter(const aFields: TSQLFieldBits=[0..MAX_SQLFIELDS-1]): boolean; overload; virtual;
    {{  filter the specified fields values of the current TSQLRecord instance
    - this version will call the overloaded Filter() method above
    - return TRUE if all field names were correct and processed, FALSE otherwise }
    function Filter(const aFields: array of RawUTF8): boolean; overload;

So if you set a TSynFilterUppercase, calling Rec.Filter will perform all filters to Rec fields.

  /// a custom filter which will convert the value into Upper Case characters
  // - UpperCase conversion is made for ASCII-7 only, i.e. 'a'..'z' characters
  // - this version expects no parameter
  TSynFilterUpperCase = class(TSynFilter)

Offline

#9 2012-04-05 08:33:59

coblongpamor
Member
From: Bali-Indonesia
Registered: 2010-11-07
Posts: 130
Website

Re: How to use TSynFilterOrValidate

Ok. on the other project i test, the TSynFilterUppercase work as expected.
i'l try to recheck what is wrong on my code.

Offline

Board footer

Powered by FluxBB