Get up to 80 % extra points for free! More info:

Lesson 9 - Building form framework for PHP - HtmlBuilder

In the previous lesson, Building form framework for PHP - Motivation, we discovered that by using a form framework we are able to program form functionality several times faster. In today's tutorial, we'll make a simple little class used to render HTML code.

Motivation

Although MVC is great, sometimes, we'll find ourselves in situations where we need to render small HTML fragments often. In this case, the amount of presentation logic will exceed the amount of pure HTML code. We could use templates, however, it wouldn't always be effective. Therefore, we'll render HTML directly using a simple class.

Let's go over a basic, little example. The goal is to render the HTML code for the following element:

<label for="car-brand">Car 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>

We want the select element to be rendered by a method in PHP. For now, we'll imagine that said method would be implemented as static on a helper class, which we would call like this:

<?= FormHelper::renderSelect('car-brand', 'Car brand', true, array(
    'Ford' => 'ford',
    'BMW' => 'bmw',
    'Audi' => 'audi',
)) ?>

We'll absolutely need a method like this since we often retrieve "option" arrays from the database, meaning that we wouldn't be able to get by using static HTML.

The actual implementation of said method could look something like this:

<?php
public static function renderSelect($name, $title, $required, $data)
{
    $html = '<label for="' . htmlspecialchars($name) . '">' . htmlspecialchars($title) . '</label>';
    $html .= '<select name="' . htmlspecialchars($name) . '" id="' . htmlspecialchars($name) . '" ';
    if ($required)
        $html .= 'required="required"';
    $html .= '>';
    foreach ($data as $title => $value)
    {
        $html .= '<option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($title) . '</option>';
    }
    return $html . '</select>';
}

I omitted the selection functionality so as to keep things simple. The code looks amateurish and suffers from a lot of issues. The HTML is not highlighted correctly since it's an ordinary string. On top of that, having to sanitize values manually using the htmlspecialchars() PHP function and concatenating the string into the $html variable makes it all very confusing.

HtmlBuilder

The HtmlBuilder class is based on the SAX principle, which is used to generate XML documents. It encapsulates the class, hence, keeps it separate from the HTML syntax. It also simplifies generating code in order to work with individual elements. The HTML is generated internally (sort of like how it was in the example shown above). The advantage to setting things up like this is that we can work with it like regular elements and not like a string.

Here's what rendering the select element would look like using the HtmlBuilder:

public static function renderSelect($name, $title, $required, $data)
{
    $builder = new HtmlBuilder();
    $builder->addValueElement('label', $title, array(
        'for' => $name,
    ));
    $selectAttributes = array(
        'name' => $name,
        'id' => $name,
    );
    if ($required)
        $selectAttributes['required'] = 'required';
    $builder->startElement('select', $selectAttributes);
    foreach ($data as $title => $value)
    {
        $builder->addValueElement('option', $title, array(
            'value' => $value,
        ));
    }
    $builder->endElement();
    return $builder->render();
}

We only work with elements and arrays of their attributes. Everything is concatenated and sanitized automatically, so we minimize the odds of us messing things up. The resulting HTML code is identical to the one from the beginning of the article.

Implementation

Let's prepare the HtmlBuilder class and add two private properties into it. The properties will be the string being concatenated and the stack of opened paired tags. Thanks to the stack, we'll know which element we have opened last and will enable us to close it easily without specifying its name.

class HtmlBuilder
{
    private $html = '';
    private $elementStack = array();

}

Remember to document the class properly. Next, we'll add a private method which renders an element:

private function renderElement($name, $htmlParams, $pair)
{
    $this->html .= '<' . htmlspecialchars($name);
    foreach ($htmlParams as $key => $value)
    {
        $this->html .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
    }
    if (!$pair)
        $this->html .= ' /';
    $this->html .= '>';
    if ($pair)
        array_push($this->elementStack, $name);
}

The method will take the name of the HTML element, its HTML parameters and whether it's a paired element as parameters. The method creates an HTML string and concatenates it to the resulting HTML. If the element is paired, we add it to the stack (so we know that it's now open).

We'll create several public methods that will use this private one:

public function addElement($name, $htmlParams = array())
{
    $this->renderElement($name, $htmlParams, false);
}

public function startElement($name, $htmlParams = array())
{
    $this->renderElement($name, $htmlParams, true);
}

public function addValue($value, $doNotEscape = false)
{
    $this->html .= $doNotEscape ? $value : htmlspecialchars($value);
}

public function endElement($name = null)
{
    if (!$name)
        $name = array_pop($this->elementStack);
    $this->html .= '</' . htmlspecialchars($name) . '>';
}

function addValueElement($name, $value, $htmlParams = array(), $doNotEscape = false)
{
    $this->startElement($name, $htmlParams, true);
    $this->addValue($value, $doNotEscape);
    $this->endElement();
}

Let's go over what each method does:

  • addElement() renders a simple unpaired element.
  • startElement() opens a paired element.
  • addValue() adds the HTML code either into the currently opened element or to the resulting HTML. We can specify whether the value should be converted to entities or not.
  • endElement() closes the last opened paired element. We can also specify the element name in a case another instance opened it. Otherwise, we wouldn't have any information on the element we need to close.
  • addValueElement() opens a paired element, inserts a value into it and then closes it.

The resulting HTML is returned by the render() method:

public function render()
{
    return $this->html;
}

That's it, we're done! The documented class is available for download in the attachment below. In the next lesson, FormControl - Parent of form controls in PHP, we'll start working on the parent for all form controls.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 26x (3.71 kB)
Application includes source codes in language PHP

 

Previous article
Building form framework for PHP - Motivation
All articles in this section
Libraries for PHP
Skip article
(not recommended)
FormControl - Parent of form controls in PHP
Article has been written for you by David Capka Hartinger
Avatar
User rating:
1 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities