Lesson 8 - Building form framework for PHP - Motivation
Welcome to this new lesson on creating libraries for PHP! Today, we'll start working on a larger, yet still minimalistic library, which will handle all issues regarding forms for us. Forms in web applications represent a significant part of the whole. Therefore, a form framework will save us a lot of time and effort. Its creation will take up several lessons and it'll be slightly advanced in functionality. With this in mind, I've decided to dedicate all of today's lesson to motivate you all so you could see all of the benefits that come with it. To be clear, the result will be more of a small framework than a library, however, I'll use these terms interchangeably.
Forms in plain PHP
Let's go over how many things we have to do in a plain PHP in order to save values from several form fields into the database. We'll demonstrate everything on a simple form that simulates ordering a car:
HTML code
When we make forms, we usually start with the HTML code. We want the form to look nice and responsive, so we'll have to add additional div and span elements. We'll use HTML 5 attributes for client-side validations, which will keep things as simple as possible. Our HTML code could look something like this:
<form method="POST" id="car-order" class="fancyform"> <div class="form-component"> <label for="name">Name</label> <input name="name" id="name" required="required" type="text"/> <div class="clear"></div> </div> <div class="form-component"> <label for="email">Email</label> <input name="email" id="email" type="email" /> <div class="clear"></div> </div> <div class="form-component"> <label for="car-brand">Brand</label> <select name="car_brand" id="car_brand" required="required"> <option value="ford">Ford</option> <option value="bmw">BMW</option> <option value="audi">Audi</option> </select> <div class="clear"></div> </div> <div class="form-component"> <label for="drivetrain">Drivetrain</label> <select name="drivetrain" id="drivetrain" required="required"> <option value="front-wheel">Front-wheel</option> <option value="rear-wheel">Rear-wheel</option> <option value="4x4">4x4</option> </select> <div class="clear"></div> </div> <div class="form-component"> <label for="payment_method">Payment method</label> <div name="zpusob_platby" id="payment_method" class="radio-horizontal"> <span> <input name="payment_method" id="payment_method1" value="wiretransfer" type="radio" checked="checked"/> <label for="payment_method1">Wiretransfer</label> </span> <span> <input name="payment_method" id="payment_method2" value="check" type="radio"/> <label for="payment_method2">Check</label> </span> <span> <input name="payment_method" id="payment_method3" value="cash" type="radio"/> <label for="payment_method3">In cash</label> </span> </div> <div class="clear"></div> </div> <div class="form-component"> <label for="note">Note</label> <textarea name="note" id="note"></textarea> <div class="clear"></div> </div> <div class="form-component"> <label for="equipment">Equipment</label> <div name="equipment" id="equipment" class="radio-horizontal"> <span> <input type="checkbox" name="abs" value="1" id="equipmentabs"/> <label for="equipmentabs">ABS</label> </span> <span> <input type="checkbox" name="wipers" value="1" id="equipmentwipers"/> <label for="equipmentwipers">Windshield wipers</label> </span> <span><input type="checkbox" name="asr" value="1" id="equipmentasr"/> <label for="equipmentasr">ASR</label> </span> <span> <input type="checkbox" name="eps" value="1" id="equipmenteps"/> <label for="equipmenteps">EPS</label> </span> </div> <div class="clear"></div> </div> <div class="form-buttons"> <input name="order" id="order" value="Order" type="submit"/> </div> </form>
The code is 74 lines long and only displays seven fields, it's not very effective. Things could only get worse from then on (without using a framework).
Validation
We'd then have to re-validate the data on the server-side. We have to because we can't rely solely on the client's validation. The main purpose is for clients to see what they entered wrong, if applicable. To avoid storing inconsistent data into the database, we'd have to perform a simple server-side validation:
if (isset($_POST['name']) && ($_POST['name']) && (!isset($_POST['email']) || (preg_match('/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/u', $_POST['email']))) && isset($_POST['car_brand']) && ($_POST['car_brand']) && isset($_POST['drivetrain']) && ($_POST['drivetrain']) && isset($_POST['payment_method']) && isset($_POST['note']) && isset($_POST['equipment']))
We're now at 81 lines.
Saving values into database
Then, we'd have to save the order into the database:
Db::dotaz(' INSERT INTO `order` (name, email, car_brand, drivetrain, payment_method, note, equipment) VALUES (?, ?, ?, ?, ?, ?, ?) ', $_POST['name'], $_POST['email'], $_POST['car_brand'], $_POST['drivetrain'], $_POST['payment_method'], $_POST['note'], $_POST['equipment']);
That's 86 lines total needed to add seven fields into the database!
Form framework
Now that that's been established, let's go over what the same, exact form would look like using our form framework.
Form definition
We'll create the form as an instance of the Form class and add individual fields:
$form = new Form('car-order'); $form->addTextBox('name', 'Name', true); $form->addEmailBox('email', 'Email', false); $form->addComboBox('car_brand', 'Brand', true) ->setValues(array( 'Ford' => 'ford', 'BMW' => 'bmw', 'Audi' => 'audi', )); $form->addListBox('drivetrain', 'Drivetrain', true, false) ->setValues(array( 'Front-wheel' => 'front-wheel', 'Rear-wheel' => 'rear-wheel', '4x4' => '4x4', )); $form->addRadioGroup('payment_method', 'Payment method', true) ->setValues(array( 'Wiretransfer' => 'wiretransfer', 'Check' => 'check', 'In cash' => 'cash', )); $form->addTextArea('note', 'Note', false); $form->addCheckList('equipment', 'Equipment') ->setValues(array( 'ABS' => 'abs', 'Windshield wipers' => 'wipers', 'ASR' => 'asr', 'EPS' => 'eps', )); $form->addButton('order', 'Order');
At this point, we have 29 lines of code for seven fields, which is a good ratio. All of the additional lines simply define the items that are to be chosen.
HTML code
The HTML code will be generated automatically. All we'll have to do is call the render() method on the form instance:
<?= $form->render() ?>
Validation
The server-side validations will be performed automatically after the first attempt to retrieve values from the form occurs.
Saving values into database
If we use the form framework along with ICT.social's database wrapper, we'd be able to save data into the database like this:
Db::insert('order', $form->getData());
Total length: 31 lines as opposed to 86 lines without using the framework. We've removed more than 60% of the code and got the same results. We could make two more forms in the same amount of time it would take to make one otherwise. The example was greatly simplified. The difference would be even more apparent on larger, more complex forms.
Additional features
The form framework has many other features which I didn't want to throw at you quite yet:
- Additional fields - Additional form fields such as
ListBoxes, FileBoxes, PasswordBoxes and so on.
- Partial rendering - A form can also be partially rendered in
case you want to set up a different layout, insert an image or headings
somewhere in a form, split the form into multiple pages, add special JavaScript
buttons, etc.
- Multiple forms - The library can easily handle multiple forms
on a single page.
- Filtering data - You can use the getData() method to retrieve
a specific part of the data. This method is useful when there are fields in a
form that you don't want to save into the database, e.g. a captcha.
- Filling data in - You can fill a form in with the data
retrieved from a query using the setData() method. This method comes in handy
when editing entities.
The most important thing to take away from this lesson is that the library is minimalistic and is designed in a way that makes extending and modifying it simple. I hope this lesson has motivated you enough to want to actually make this framework. In the next lesson, Building form framework for PHP - HtmlBuilder, we'll create the first utility class for the form framework, the HtmlBuilder class.