All web developers will at some point need to program their own form. Whether it is for database submission, emailing, or for some other purpose, forms represent the primary means of enabling a user to send data to an application. As a result, it is important to have control over the data collected by your forms, which will aid in the creation of streamlined, error-free applications.
This article assumes the reader has some prior knowledge of object-oriented design and/or programming.
Why Validate Forms Server-Side?
A modular approach to a design such as this will be crucial for the application's success. Regardless of what you may hear, you should not write a line of code without thoroughly planning out your design. A good technique will usually result in about 70% of your time spent on design (and diagrams) with the remaining 30% on programming. It will be a good step if you can draw out how your application will function, and be able to explain it to others.
So where should we start? We need to design a core object first. In this case we will call it a ValidationSet. As the name hints, our ValidationSet will store all of our form Validators. Each Validator will determine if the data passed was valid according to its own set of rules. If the data is invalid, we will need to return a ValidationError object to our ValidationSet.
There is one important thing to note here, object names such as "Validators" should never be plural. This is a hint that there is more involved than just making one object. We will need to develop all kinds of validator objects and while they will be specific to what kind of validation purpose they serve, they will all need to be added to a single ValidationSet. Object-oriented theory tells us that we should therefore use a superclass. The reason for this is that our ValidationSet can be coded to expect a single type of object (Validator in this case), which gives a layer of abstraction between the object representing the form validation as a whole and the objects doing the actual work. Those of you familiar with design patterns may recognize this as code to an interface, not an implementation. Objects such as an EmailValidator or PhoneValidator are examples of different types of form validators we may require. Figure 1.1 shows us what the overall validation system may look like drawn out:
Now that we have our overall application thought out, we can continue with diagramming our individual objects. If you haven't dealt with object diagramming before that's okay. Diagramming is a simple task where you come up with all your objects and the methods they will contain. Although there are several programs out there that will help you diagram, I prefer the old pencil and paper approach.
We will begin with diagramming the ValidationSet object. Since the ValidationSet will be storing all of our validators and ValidationErrors, we will need methods to add them. We must also be able to invoke a method which will tell all of the stored validators to perform their individual validations. Furthermore, we will need to know if the application has found any errors, and provide methods for returning a single error, or all of them. Another good thing to plan for is allowing our validators the capability to run validations against a database. Rather than storing a database object in each individual validator, we can just store it once in our ValidationSet. As a result, we will need methods to get and set our database connection object as well. Gathering this information, we can draw out an object diagram for our ValidationSet similar to that shown in Figure 2.1:
We will continue on with diagramming our Validator superclass. It is important to identify methods that all Validator objects will inherit. Since our ValidationSet object stores all of our errors (and each Validator must be capable of sending it an error), we will need a way to access that objects methods by using a reference. This reference will also give our validators the ability to retrieve the database connection object mentioned above. All of our validators must also be able to retrieve a form fields submitted value (the actual form fields name will be sent individually to each Validators constructor). These two methods will comprise our Validator object as shown in Figure 2.2:
Specific validators, such as our EmailValidator will be simple in their design. They will be limited to a constructor and a validate method. Remember, because these objects will extend our Validator object, they will inherit those methods as well. Figure 2.3 shows us what a specific validator will look like:
If an error is found, a ValidationError will be instantiated from a specific validator object (in the validate method mentioned above), and then sent to the ValidationSet. It will comprise both a field name and an error message associated with that field. As a result, this object will contain both a method for retrieving the field name, and another for the error message, as shown in Figure 2.4: