C++ Builder Tutorials

C++Builder - ClientDataSet, part 3

Validating

Validating a database record includes two aspects:

  1. The integrity of the database has to be ensured. That is, we make sure that:
    • there are no records with the same "key" field (the field "ID" in our case);
    • the key field is not empty;
    • the key field obeys to certain rules.
  2. Only valid data should be accepted, such as: no empty fields and illegal values.

Preparations

  1. If you haven't done so already, create a new folder \CppProjects on your disk.
  2. Create folder XMLDatabase2 "under" \CppProjects.
  3. Download XMLDatabase2.zip (source code for part 2 and 3) and unzip it to folder XMLDatabase2.
  4. Compile and run the application, immediately stop it and next copy Articles.xml from folder XMLDatabase2 to folder XMLDatabase2\Win32\Debug.

Validating the ID field

We've split our checking into two parts: firstly, we check the ID with the function ValidID().
Next, we'll check the 3 other fields.

Before posting a record, we check if the ID contains 4 characters. That's easy.

For checking on a double ID however, we have to search with the function FindKey() if the dataset already contains a record with this ID. But this poses two problems:

  1. cdsArt is in the data-state dsInsert or in dsEdit, and moving to another record would automatically post the actual record and set the the state to dsBrowse.
  2. cdsArt is ordered ("indexed") on the field Name, but to find an ID, it must be indexed on ID.

Solution: we use a second clientdataset that is a clone of cdsArt, aptly named cdsArtClone. We index the clone dataset on ID.

bool TForm1::ValidID(String& Err)  // Err is passed by REFERENCE
{
  bool Result = true;
  String ID = cdsArt->FieldByName("ID")->AsString;
  int LenID;
  Err = "";
  LenID = ID.Length();
  Result = (LenID == 4);
  if (! Result)
    Err = " Invalid ID: must be 4 characters ";
  else {
    cdsArtClone->CloneCursor(cdsArt, true, false);
    cdsArtClone->IndexFieldNames = "ID";
    // Note double brackets with ARRAYOFCONST
    if (cdsArtClone->FindKey(ARRAYOFCONST((ID)))) {
      if (cdsArt->State == dsInsert) {
        Result = false;
        Err = "Invalid, ID already exists";
      }
      else {         // editing
        if (cdsArt->FieldByName("ID")->AsString != OldID) {
          Result = false;
          Err = "Invalid, ID already exists";
        }
      }
    }
  }
  return Result;
}

Note that in addition to returning a value of true / false, this function also sets a message in parameter Err, that was passed by reference, thus modifying the original value of Err in function btnOKClick().
Let's look how ValidID() is used:

void __fastcall TForm1::btnOKClick(TObject *Sender)
{
  String Err = ""; // for receiving error messages
  bool ValidRecord = ValidID(Err);
  // more statements...
  
  if (ValidRecord) {
    // post record, and so on...
  }  

  else {         // invalid ID
    // display error message  
    if (cdsArt->State == dsInsert)
      ShowStatus("inserting", Err);
    else
      ShowStatus("editing", Err);
  }
}

Validating Name, Price and Stock

Function ValidData() checks if the field Name is not empty, and if there is no negative value in Price nor in Stock:

bool TForm1::ValidData(String& Err)  // Err is passed by REFERENCE
{
  bool Result = true;
  Err = "";
  Result = (Trim(cdsArt->FieldByName("Name")->AsString) != "");
  if (! Result)
    Err = "Invalid, NAME can not be empty";
  else {
    Result = ((cdsArt->FieldByName("Price")->AsFloat) >= 0);
    if (! Result)
      Err = "Invalid, PRICE can not be negative";
    else {
     Result = ((cdsArt->FieldByName("Stock")->AsInteger) >= 0);
     if (! Result)
       Err = "Invalid, STOCK can not be negative";
    }
  }
  return Result;
}