netefx internet services
Ihre Daten
Our silverstripe modules:

NetefxValidator
Advanced form validation

silverstripe flexslider
A slider module for silverstripe 3
netefx internet services
XHTML 1.0 Validation passed

NetefxValidator (0.7)

0.7 new class names, lots of changes, no more backward compabiltity
0.45 new library rule "checkboxes_no_overlapping"
0.44 constants added (request from zauberfisch). So you can use NV_BETWEEN as rule name. All constants beginn with NV_
0.43 optional error type (thanks to zauberfisch), new rule "exists"
0.42 changes the way validation for numbers with a custom decimal mark (e.g. ",") is done
0.41 fixes a SQL Injection vulnerability in the UNIQUE rule. (Thanks to ajshort).

Form validation in Silverstripe 2.4

When you build forms in silverstripe, you can use a RequiredFields-validator (official documentation). This validator defines which fields have to be filled in. But often this is not enough. If you want to make sure, that your form (frontend or backend) is filled correctly, your rules have to be more restrictive. There are specific fields in sapphire, like the EmailField() (official API documentation), which have a validationrule themeselves. But it doesnt make sense to define a separate class for every field of your form just for validation purposes.

What is does:

You can use the NetefxValidator to perform any validation you want. You can write NetefxValidatorRules of several types and add them to the validator. Each rule belongs to a form field, has a type, parameters and an error message, which is displayed to the user, if the rule does not validate. There are different types of rules, like REQUIRED rules, which simply express, that a field has to be filled, numeric rules, which are used to compare an input field to a fixed value or to other input fields, REGEXP rules, which expect a regular expression to check for and FUNCTION rules, in which you can use your self-written functions or library functions. Finally you can combine rules to a new rule using logical rule combination.

What is missing:

- There is no Javascript validation at all.
- The validator is not integrated in module Userforms.

Comments

For comments, suggestions, and asking for features please use this forum thread.

Documentation (based on a sample form)

To have a generally understandable example, let us say, we have three holiday flats and want to build a simple website for booking these holiday flats. The flats are located in Paris, London and Berlin. So we build a simple form, giving users the opportunity to directly book one of the flats.

Creating the form as usual

We realize the booking procedure by creating a standard silverstripe form first. It looks like the following. As you can see, we dont want the RequiredFields validator in line 18.

                        public function bookingForm() {
                                 
                                $fields = new FieldSet(
                                    new TextField("LastName", "Last Name"),
                                    new TextField("FirstName", "First Name"),    
                                    new EmailField("Email"),        
                                    new DropdownField('Flat',
                                                   'Select flat',
                                    array('London' => 'London',
                                          'Paris' => 'Paris',
                                          'Berlin' => 'Berlin')),

                            ...
                                );
                                
                                $actions = new FieldSet(new FormAction("Submit", "Submit"));
                                
                                // $validator = new RequiredFields(array('LastName', 'FirstName'));
                                   
                                $form = new Form($this, "bookingForm", $fields, $actions, $validator);
                                
                                return $form; 
                        }
                    

Adding validation rules

So far, we do not have any validation yet. At first, we want to check, if the fields Last Name and First Name are filled. So we build rules to define that these fields are required:

                        ...
                        $rule_LastName_Required  = new NetefxValidatorRuleREQUIRED("LastName", "Please enter your last name", 'error');
                        $rule_FirstName_Required = new NetefxValidatorRuleREQUIRED("First Name", "Please enter your first name", 'error');                      
                        ...
                    

The parameters of a rule are the following:

  1. suffix of the class name: the type of the rule (in this case “REQUIRED”)
  2. first parameter: the name of the field, the rule contains to
  3. second parameter: the message to display to the user, if validation fails
  4. third parameter: the type of the error message (“error” is default)
  5. forth parameter: rule specific parameter or array of parameters (depending on rule type; in case of “REQUIRED” not applicable)

Note that for rules, which expect exactly one parameter as forth parameter, you can use one parameter as well as an array containing one parameter

To add the two validation rules to our form, we create a validator managing these rules and add this validator to our form:

                        ...
                        $validator = new NetefxValidator($rule_LastName_Required, $rule_FirstName_Required);
                        $form = new Form($this, "bookingForm", $fields, $actions, $validator);
                        ...
                    

If we test out the validation by leaving the two required fields empty, we get our specified error message as expected:

netefxvalidator_screen1.gif (5.530 bytes)

Now you can simply extend the validation by defining new rules. Don´t forget, that you do not only have to define the rule by creating a new NetefxValidatorRule, but also have to add the new defined rule to the constructor of the NetefxValidator.

Different Error Types

In case, you want to distinguish between different kind of errors, e.g. for showing different background color to the user, you can add an error message type to each rule. As default, error messages have the type "error". In the following example, we define two further types of error messages and call them "notice" and "warning".

                        ...
                        $rule_LastName_Required = new NetefxValidatorRuleREQUIRED("LastName", "Please enter your last name", "notice");
                        $rule_FirstName_CharBetween = new NetefxValidatorRuleCHARACTERSBETWEEN("FirstName", "Please enter between 2 and 20 characters", "warning", array('2','20'));
                        ...
                    

Now you have adapt your style by giving our self-defined classes "notice" and "warning" another background color as "error". Then can see different kind of error messages displayed to the user like in the following screenshot.

validation error types

Available validation rules

In the following we describe the different type of rules, you can define, giving an example for each type of rule. (Just click the headline.)

REQUIRED and EMPTY rules

The most simple type of validation is to specify, that a field is required, i.e. is must not be empty. In the last section we already gave an example for the rule type REQUIRED. We repeat the example here to guarantee completeness:

                                ...
                                $rule_LastName_Required = new NetefxValidatorRuleREQUIRED ("Last Name", "Please enter your last name", 'error');  
                                ...
                            

The opposite of a REQUIRED rule is an EMPTY rule. It validates if and only if the content of a field is empty. Obviously, an EMPTY rule makes no sense as stand-alone rule, since you can let out a field at all, if it must not be filled in by the user. But you can use an EMPTY rule in logical combination with other rules, as described below.

REQUIRED and EMPTY rules have no rule specific parameters.

numeric rules: GREATER, SMALLER, GREATEREQUAL, SMALLEREQUAL, EQUALS, BETWEEN

There are 6 types of numeric rules. All of these rule check first, if the content of the field is numeric. As second step is checked, if the number fulfills the given restriction.

To give an example, let us say, we do not want to rent our flats for more than six persons. So we build a rule, that the content of the field Persons must not exceed 6:

                                ...
                                $rule_Persons_Max = new NetefxValidatorRuleSMALLEREQUAL ("Persons", "Our flats are suitable for at most six persons", 'error', '6');
                                ...
                            

If we want to restrict the minimum number as well, you can use a BETWEEN rule alternatively:

                                ...
                                $rule_Persons_Between = new NetefxValidatorRuleBETWEEN ("Persons", "We rent our flats only for 2-6 persons", 'error', array('2','6'));
                                ...
                            

You can not only compare the content of the field with a constant value, but also with the content of other fields. If we have an additional field to enter the number of children, you can add two more rules to guarantee, that the number of children must not exceed the number of total persons. Since we do not want to rent our flat to children, we add one more rule to guarantee, that we have at least one adult.


                                ...
                                $rule_Children_1 = new NetefxValidatorRuleSMALLEREQUAL("Children", "To rent our flat, you need at least one adult", 'error', '@Persons~-1');  
                                $rule_Children_2 = new NetefxValidatorRuleSMALLEREQUAL("Children", "wrong input: the number of children exceeds number of persons ", 'error', '@Persons~');
                                ...
                            

In the expression, the content of the field is compared to, you can add links to other fields by enclosing the name of the other field with @ and ~ (like @Persons~ in the example above). As you can see in the first example, you can use any numeric expressions, which can be evaluated by the PHP function eval. Note that all numeric rules do not validate, if the input of the field, the rule belong to or any other field, that is linked to in the expression, is not numeric.

At first view, the second rule seems to be unnecessary, since the first rule does also not validate, if the number of children exceeds the number of persons. But the error message of both rules differ, so the user gets a more specific error message. Note that the error message of the last rule, that does not validate is displayed to the user. So if you swap the rules at adding to the validator, the lower rule is indeed unnecessary.

Remember that you have to put exactly one parameter for GREATER, GREATEREQUAL, SMALLER, SMALLEREQUAL and EQUALS and an array of exactly two parameters for BETWEEN.

As further optional parameter, you can add the decimal mark, which is expected to be entered by the user. You can choose between a decimal point (.) which is the default value and a decimal comma (,) or any other decimal mark with exactly one character. Note, that the input must not contain any other character except digits (0-9) and the chosen decimal mark. So an input which a digit group separator (12,345.67 e.g.) is not allowed.

The decimal mark is expected as second parameter for GREATER, GREATEREQUAL, SMALLER, SMALLEREQUAL and EQUALS and as third parameter for BETWEEN. In our example, we want to offer extra long beds to our guests up to a height of 2.15 meters. If we have e.g. a french website for our flats, we expect the user to enter his height with a comma as decimal mark. So we add the following rule with a comma as second parameter:

                                ...
                                $rule_BedSize = new NetefxValidatorRuleSMALLEREQUAL("BedSize", "We have no beds longer than 2,15 meters.", 'error', array('2.15',','));  
                                ...
                            

If we test out the validation by entering a number above 2.15 with a comma as decimal mark, we get our specified error message as expected:

netefxvalidator_screen4.png

Since the rule does not validate, if the user enters a wrong number format, you should either change the error message (e.g. "We have no beds longer than 2,15 meters or you entered a wrong number format") or add an additional rule, which only checks for a correct number format.

textual rules: TEXTCONTAINS, TEXTIS, ISONEFROM, ISNOTONEFROM, TEXTEQUALS

There are three types of textual rules for an input field.

The first, namely TEXTCONTAINS is used to check, if the text in an input field contains a given value. The only parameter is the text, which has to be contained by the input.

The second TEXTIS is used, if the text has to be a specified value. In most cases, this is not used for a TextField, but for a DropdownField, where you have to select a certain value. Furthermore it is normally combined with other rules in a logical rule combination. An example for a TEXTIS rule follows:

                                ...
                                $rule_ParisSelected =  new NetefxValidatorRuleTEXTIS ("Flat", "you have to select Paris", 'error', "Paris");
                                ...
                            

The only parameter is the text, which has to be typed in to the input field or chosen form a dropdown field resp.

If you have not only one value, like in TEXTIS, but several values, of which the user has to input one, you can use a ISONEFROM rule. If in our example, only the flats in Paris and Berlin are available, since the flat in London is renovated, the user has to select either Paris or Berlin:

                                ...
                                $rule_OnlyParisAndBerlin = new NetefxValidatorRuleISONEFROM("Flat", "Only Paris and Berlin are available at the moment.",'error', array("Paris","Berlin"));
                                ...
                            

The parameters are all allowed texts, which has to be typed in to the input field or chosen form a dropdown field resp.

Inversely, there also exists a ISNOTONEFROM rule, which helps you to exclude some values:


                                ...
                                $rule_NotParisAndBerlin = new NetefxValidatorRuleISNOTONEFROM("Flat", "Paris and Berlin are not available at the moment.",'error', array("Paris","Berlin"));
                                ...
                            

The parameters are all texts, which are not allowed to be typed in to the input field or chosen form a dropdown field resp.

The last textual rule is TEXTEQUALS, which simply checks, if the inputs of two fields, match. This is normally used, if the used has to enter a new password, like in the following example:

                                ...
                                $rule_password_Match = new NetefxValidatorRuleTEXTEQUALS("Password2", "the passwords does not match", 'error', 'Password');
                                ...
                            

The only parameter is the name of the field, the input field has to match.

textual length rules: MINCHARACTERS, MAXCHARACTERS, CHARACTERSBETWEEN

If you want to restrict the length of an input field, you can use textual length rules. In the following example, the first rule restricts the length of the input of the first name to at least two characters. The second rule restricts the length of the input of the first name to at most twenty characters.

                                ...
                                $rule_FirstName_MinChar = new NetefxValidatorRuleMINCHARACTERS("FirstName", "Please enter at least two characters", 'error', '2');
                                $rule_FirstName_MaxChar = new NetefxValidatorRuleMAXCHARACTERS("FirstName", "Please enter at most twenty characters", 'error', '20');
                                ...
                            

If you want to restrict the minimal and maximal length of an input field, like in the example, you can alternatively use a CHARACTERSBETWEEN rule:

                                ...
                                $rule_FirstName_CharBetween = new NetefxValidatorRuleCHARACTERSBETWEEN("FirstName", "Please enter between 2 and 20 characters", 'error', array('2','20'));
                                ...
                            

The first parameter is here the minimum and the second is the maximum value of the length of the input field.

UNIQUE rule

The UNIQUE rule is the only rule type, which accesses the database. If a new data set should be added to the database, e.g. if a new user wants to register to the system, and the data set has unique fields, you can check, if the unique restriction is fulfilled. In case of registration of a new user, the e-mail address may not be allocated to another user. The rule looks as follows:

                                ...
                                $rule_email_NotAlreadyAssigned = new NetefxValidatorRuleUNIQUE ("EMail", "e-mail already assigned to another user", 'error', array('EMail','Member'));
                                ...
                            

The two parameters are the name of the field and the name of the class, for which the input has to be unique.

Note, that you can use a UNIQUE rule not only if you create a new data set, but also if you edit a data set. If you edit a database record, e.g. in Modeladmin, your form does also contain the ID of the record. And If an ID field is in your form, the unique rule will only return false if you enter a value which is already stored but is not this field itelsef.

REGEXP rule

A powerful possibility to validate single input fields for fulfilling a certain syntax are regular expressions. To give an example, let us say, the user of our website should get the possibility to enter a discount code from our marketing partner. In our example, we cannot check the validity of the discount code at the moment, but at least, we can check the syntax: two letters followed by five digits.

                                ...
                                $rule_DiscountCode = new NetefxValidatorRuleREGEXP ("DiscountCode", "invalid discount code", 'error', "/^[A-Z]{2,2}[0-9]{5,5}$/");
                                ...
                            

The only parameter is needed is the expression to check the input. Remember, that you have to put the expression into an array.

If the input of a discount code is optional, which is obviously reasonable in this example, you have either to change the expression to accept an empty string as well or take use of logical combination of rules, as described in the next section.

logical rule combination: AND, OR, NOT, IMPLIES and XOR

Remember the example from last section, where we have a field for an optional discount code, which either must be left empty or must fulfill a special syntax. So we first create rules for the two cases. The first is the same as in the chapter before, the second simply describes, that the field has to be left empty.

                                ...
                                $rule_DiscountCode_Syntax = new NetefxValidatorRuleREGEXP ("DiscountCode", "message is irrelevant", 'error', "/^[A-Z]{2,2}[0-9]{5,5}$/");
                                $rule_DiscountCode_Empty  = new NetefxValidatorRuleEMPTY ("DiscountCode", "message is irrelevant", 'error');
                                ...
                            

Now we combine the to rules to a new rule using an OR rule:


                                ...
                                $rule_DiscountCode = new NetefxValidatorRuleOR("DiscountCode", "invalid discount code", 'error', array($rule_DiscountCode_Syntax,$rule_DiscountCode_Empty));
                                ...
                            

As you can see, the (two or more) rules, of which the OR rule is combined, are given as parameter in one array. An OR rule validates, if at least one of the rules, of which the rule is combined validates.

Note that you only have to add $rule_DiscountCode to the validator, but not the rules $rule_DiscountCode_Syntax and $rule_DiscountCode_Empty, of which the OR rule is combined. Therefore, the error message of these two rules are irrelevant.

Inversely to the OR rule, you also have an AND rule, which validates, if all of the (two ore more) rules, of which the AND rule is combined validate. Since it makes no difference, if you add an AND rule to the validator or several single rules (having the same error message), an AND rule is often used, if it is itself part of another logically combined rule, e.g. if the user has to enter either account number and bank code or only a credit card number.

If you want to exclude some special input or in generally in cases, where it is easier to word, which input is not allowed rather than what is allowed, you can use a NOT rule. A NOT rule, validates, if the exactly one rule, which is given as the only member of the parameter array does not validate. To give an example: if we would not have the construct of an EMPTY rule, we could formulate the rule $rule_DiscountCode_Empty from above alternatively:

                                ...
                                $rule_DiscountCode_Required = new NetefxValidatorRuleREQUIRED("DiscountCode", "message is irrelevant", 'error'); 
                                $rule_DiscountCode_Empty    = new NetefxValidatorRuleNOT("DiscountCode", "message is irrelevant", 'error', $rule_DiscountCode_Required);
                                ...
                            

Although you can express any logical expression using AND, OR and NOT, an often used and helpful rule is the IMPLIES rule. An IMPLIES rule is used to express, which further condition has to be fulfilled, if a conditon is fulfilled. To give an example. All of our flats are suitable for six persons, but since the flat in Paris is a little bit smaller, it is only suitable for at most five persons. So we have to formulate a rule, that if the flat in Paris is selected, the number of persons must not exceed five:

                                ...
                                $rule_ParisSelected         = new NetefxValidatorRuleTEXTIS ("Flat", "message is irrelevant", 'error', "Paris");
                                $rule_MaxFivePersons        = new NetefxValidatorRuleSMALLEREQUAL ("Persons", "message is irrelevant", 'error', '5');
                                $rule_MaxFivePersonsInParis = new NetefxValidatorRuleIMPLIES ("Persons",  "Our flat in Paris is suitable for at most five persons.", 'error', array($rule_ParisSelected,$rule_MaxFivePersons));
                                ...
                            

An IMPLIES rule needs an array with exactly two parameters. If the rule, which is given as first parameter does not validate, the IMPLIES rule validates independently from the second parameter. But if the rule, which is given as first parameter validates, the IMPLIES rule only validates, if the rule given as second parameter validates, too.

Similar to the IMPLIES rule, you can also use a XOR rule. A XOR rule validates, if and only if exactly one of the two rules, which are given as parameter, validates.

EXISTS rule

In some cases you might want to build up your form dynamically by adding and removing fields depending of the input of the user. If you want to specify such a field as required, the user will get an error, if the field is in fact not there. So we need a chance to define a fact like the following: “if the field is in the form, then it is required to be filled out by the user”. This can be realized by our new EXISTS rule in combination with IMPLIES like in the following example:

                                ...
                                $rule_LastName_Exists = new NetefxValidatorRuleEXISTS ("LastName", "message is irrelevant", 'error'); 
                                $rule_LastName_Required = new NetefxValidatorRuleREQUIRED ("LastName", "message is irrelevant", 'error');
                                $rule_LastName_Required_If_Exists = new NetefxValidatorRuleIMPLIES ("LastName", "Please enter your last name", 'error', array($rule_LastName_Exists, $rule_LastName_Required));
                                ...
                            

EXISTS rules have no rule specific parameters. Note that an EXISTS rule is only useful in a logical combination with other rules.

FUNCTION rule: using library functions or writing own functions

Since there are so many possibilities to check the input of the user, it is clear, that a validator cannot anticipate all kind of possible input checks and offer suitable kind of rules. To make possible any validation you want anyway, you can use the FUNCTION rule and write your own specialized validation function. To give an example, we want to rent our flats for at least four days. We cannot validate, if the user input fulfills this restriction using the rule types presented until now. But as luck would have it, we have a library function in our NetefxValidatorLibrary, which does exactly this validation. The function looks simplified like this:

                                ...
                                static function UntilIsMinDaysAfterFrom ($data, $args) {            
                                       $timestamp_dateFrom  = strtotime($data[$args["dateFrom"]]);
                                       $timestamp_dateUntil = strtotime($data[$args["dateUntil"]]);
                                       $days_dif = ($timestamp_dateUntil - $timestamp_dateFrom)/86400;
                                       return ($days_dif >= $args["min"]);
                                }
                                ...
                            

As every function, which is intended to be called by a FUNCTION rule, this function gets the complete form data, i.e. the content of every field, as first parameter. As a second parameter, all parameters, which are used by the function are given as a associative array.

                                ...
                                $rule_AtLeastFourDays =  new NetefxValidatorRuleFUNCTION ("Departure", "you have to rent our flats for at least four days", 'error', 
                                														 array('NetefxValidatorLibraryDate', 'UntilIsMinDaysAfterFrom', array('dateFrom'  => 'Arrival','dateUntil' => 'Departure', 'min' => 4)));
                                ...
                            

In generally, in a FUNCTION rule you have to pass an array of the parameters as parameter. The first and the second parameter are the name of the (library) class and function you want to call. The third parameter is an associative array of parameters, you want to pass to the function. You need not care about passing the complete form data, since this is done automatically by the validator. A FUNCTION rule validates, if the called function returns true.

netefxvalidator_screen2.gif (4.866 bytes)

You can also use the function DateIsMinDaysAfterToday, if you want to enforce the entered date to be at least some days in the future. For example, if you want to have the arrival at least five days in the future to have enough time to prepare the flat, you can add the following rule:


                                ...
                                $rule_ArrivalInFiveDays =  new NetefxValidatorRuleFUNCTION("Arrival",  "We need five days for preparation of the flat", 'error',
                                														  array('NetefxValidatorLibraryDate', 'DateIsMinDaysAfterToday', array('date' => 'Arrival',  'min' => 5)));
                                ...
                            

Inversely, you can use DateIsMinDaysBeforeToday, if you want to enforce the entered date to be at least some days in the past.

Another useful function in our library is max_number_checkboxes_checked, which can be used to restrict the number of checked checkboxes in a CheckboxSetField. In our example, we offer up to three trips for our guests, which are included in the rental fee.

                                ...
                                new CheckboxSetField("Trips","Select up to three trips (included in rental fee for the flat)",
                                array( "1" => "Museum tour", "2" => "Amusement Park", "3" => "Sightseeing bus tour", "4" => "Theater", "5" => "Adventure Swimming Pool" )),       
                                ...
                                $rule_Trips_OnlyThreeTrips =  new NetefxValidatorRuleFUNCTION("Trips", "Only three trips are included.", 'error',
                                															  array('NetefxValidatorLibraryCheckbox', 'max_number_checkboxes_checked', array('field' => 'Trips', 'max'   => 3))); 
                                ...
                            

If we have defined our CheckboxSetField as above, we can define a function rule to specify, that at most three trips can be selected. If we test out the validation by checking more than three trips, we get our specified error message as expected:

netefxvalidator_screen3.png

Inversely, you can use min_number_checkboxes_checked, if you want to enforce a minimal number of checked checkboxes.

Our libraries contain several more useful functions, like checking for allowed mime types and file sizes for a file upload. Of course, you can not only use functions from our library, but also write your own validation functions to realize any sophisticated validation functions you want.

In the following, we give an example, how to write and execute your own validation function. Imagine, you want to restrict the number of children in a very sophisticated manner. The flat in Berlin has no restriction of the number of children. In the flat in Paris, there must not be more than three children. In the flat in London, there must not be more than two children, if a discount code is entered. Otherwise, there is no restriction of the number of children. Although you could even manage this bundle of restrictions and dependencies with a lot of logically combined rules, you might find it easier to write your own validation function in our own library class:

                                ...
                                class MyLibrary {
                                      
                                      public static function MySpecialValidationFunction ($data, $args) { 

                                        //access parameters and form data                  
                                        $numberOfChildren = $data["Children"];
                                        $maximumOfChildrenParis = $args["maxChildrenParis"];
                                        $maximumOfChildrenLondon = $args["maxChildrenLondon"];
                                        
                                        //restriction for Paris    
                                        if ($data["Flat"]=="Paris") {
                                          return ($numberOfChildren <= $maximumOfChildrenParis);
                                        }

                                        //restriction for London with discount code
                                        if (($data["Flat"]=="London") && ($data["DiscountCode"]!="")) {
                                          return ($numberOfChildren <= $maximumOfChildrenLondon);
                                        } 
                                      
                                        //no restriction
                                        return true;        
                                      }
                                                   
                                }
                                ...
                            

We except the maximal number of children for Paris and London as parameters maxChildrenParis and maxChildrenLondon. We can access the complete form data in the variable $data.

Now we can use this function in our validator:

                                ...
                                $rule_Children_Restriction = new NetefxValidatorRuleFUNCTION ("Children", "Maximum of children exceeded", 'error',
                                															 array('MyLibrary', 'MySpecialValidationFunction', array('maxChildrenParis'  => 3, 'maxChildrenLondon' => 2)));
                                ...