The Singleton and Factory Patterns in PHP: Building a Form Generator Class

In this final part of the series, Alejandro Gervasio examines a point that he has not taking into consideration so far: that the layout of form elements plays a relevant role within the overall development process. With this in mind, he encapsulates the logic needed to generate web forms by defining a form generator class. This class will implement the form element factory along with all of the required form element classes.

Introduction

This is the final part of the series “The Singleton and Factory Patterns in PHP.” IN the previous article, I showed how the “formElementFactory” class may be turned into a Singleton, useful for working with a single object instance, when a program must be executed within a PHP 4 programming platform.

Also, I highlighted the advantages of using the Factory pattern to simplify tasks related to building regular web forms, by using an object-oriented approach, instead of coding them through the traditional way. Of course, the major benefit of utilizing the power of the object oriented paradigm is that once all of the form classes have been defined, generating forms within a web page is a matter of instantiating the required form objects through a factory class, and then deciding the best layout for the elements.

Stepping back to the previous part of the series, I’ve gone so far as to implement the above mentioned patterns on building object-based forms, without paying strong attention to the control of form element layout. Certainly, I wouldn’t be fully exploiting the advantages inherent in application design and code portability described throughout the series, if visual presentation is not included.

So, keeping in mind that the layout of form elements plays a relevant role within the overall development process, in this last part of the series, I’ll encapsulate the logic to generate web forms, by defining a form generator class, which will implement the form element factory together with all of the required form element classes.

With the formalities out of the way, let’s get started.

Object-based form generation: defining a form generator class

A natural place to start building object-based forms within a web page is by defining the form generator class itself. As one would expect, this class will hide the internal processing involved in instantiating form objects and expose some methods aimed specifically at giving the form a proper visual presentation. Here is what it looks like:

class formGenerator{
    // data member declaration
    private $elements=array(); // array of form elements
    private $output=”; // dynamic output
    private $elementHeader=”; // element header
    private $elementFooter='<br />'; // element footer
    private $name=’theform'; // form name
    private $method=’post'; // form method
    private $action; // form action
    // constructor
    public function __construct($elements=array()){
        if(count($elements)<1){
            throw new Exception(‘Invalid number of elements’);
        }
        // data member initialization
        $this->elements=$elements;
        $this->action=$_SERVER['PHP_SELF'];
    }
    // create form code
    public function createForm(){
        $this->output.='<form name=”‘.$this->name.'”
action=”‘.$this->action.'” method=”‘.$this->method.'”>';
        foreach($this->elements as $element=>$attributes){
            // call the abstract method formElementFactory
            $this->output.=$this-
>elementHeader.formElementFactory::createElement
($element,$attributes).$this->elementFooter;
        }
        $this->output.='</form>';
    }
    // add form part
    public function addFormPart($html='<br />’){
        $this->output.=$html;
    }
    // add element header
    public function addElementHeader($header){
        $this->elementHeader=$header;
    }
    // add element footer
    public function addElementFooter($footer){
        $this->elementFooter=$footer;
   }
    // set form name
    public function setName($name){
        $this->name=$name;
    }
    // set form action
    public function setAction($action){
        $this->action=$action;
    }
    // set form method
    public function setMethod($method){
        if($method!=’post’&&$method!=’get’){
            throw new Exception(‘Invalid form method’);
        }
        $this->method=$method;
    }
    // get dynamic form output
    public function getFormCode(){
        return $this->output;
    }
}

Now that I’ve listed the source code forming the brand new “formGenerator” class, let’s break down the code to understand each relevant method.

As you can see, the class exposes the most common data members associated with a regular web form, such as “$name”, “$action”, and “$method” attributes, along with other core properties that are used to properly render form elements. The constructor accepts a single “elements” associative array, which will be passed internally to the static method “createElement()”, in order to instantiate form objects and return their corresponding (X)HTML markup.

Certainly, after the initialization tasks are performed by the constructor, the most important method within the class is “createForm()”, since it’s responsible for generating the code that renders each form element defined from outside the class. Its source code is listed below:

// create form code
public function createForm(){
    $this->output.='<form name=”‘.$this->name.'” action=”‘.$this-
>action.'” method=”‘.$this->method.'”>';
    foreach($this->elements as $element=>$attributes){
        // call the abstract method formElementFactory
        $this->output.=$this-
>elementHeader.formElementFactory::createElement
($element,$attributes).$this->elementFooter;
    }
    $this->output.='</form>';
}

Basically, the above method builds the complete form’s code by applying the following logic: first, the opening <form> tag together with its properties are created, and second, the generation of form elements is carried out by the “formElementFactory” class. As you remember, this class was defined as abstract, so the call to its method “createElement()” is performed outside its object context. The line below shows how the markup for each element is appended to the general output:

$this->output.=$this-
>elementHeader.formElementFactory::createElement
($element,$attributes).$this->elementFooter;

Notice that aside from adding the element’s code, two additional properties, “elementHeader” and “elementFooter” are appended to the overall form’s output. The reason for adding these properties is simply to provide the class with the ability to pre-append and post-append additional HTML tags to the form element itself, allowing it to include dynamically presentational markup during the form generation process.

If this sounds rather confusing, don’t be concerned. The function of each property will become clear when I set up a functional example.

The next step in explaining how the form generator works is to analyze the rest of class methods. So, let’s jump to the few next lines to learn more about them.

{mospagebreak title=Object-based form generation (continued): explaining the remaining class methods}

As I said before, we need to take a detailed look at the rest of the class methods, so if you’re accustomed to coding web forms on a regular basis, they should be fairly easy to understand.

The “addFormPart()” method is tasked with including any required additional markup that will be rendered outside the iteration for displaying form elements. Say that some tags need to be added to the structure of the form for achieving a better layout. In this case, the method will append the required markup to the overall form’s code, as shown below:

// add form part
public function addFormPart($html='<br />’){
    $this->output.=$html;
}

As you can see, I’ve specified a default “<br />” tag to be appended when the method is invoked, but this might be easily modified to accept any other required input tag (or a set of tags).

The other two methods related to including additional markup within the iteration process for displaying form elements are “addElementHeader()” and “addElementFooter()”. Despite the fact they were mentioned previously, here are their respective definitions:

// add element header
public function addElementHeader($header){
    $this->elementHeader=$header;
}
// add element footer
public function addElementFooter($footer){
    $this->elementFooter=$footer;
}

Similar to “addFormPart()”, the above methods are useful for inserting interspersed markup before and after the code of a form element that has been appended to the form’s output. As expected, these methods implement a simple yet effective way to wrap form fields into regular containers such as <div>, <p> or <table> elements.

Having explained the core methods of the class, as well as those aimed at adding presentational functionality, the remaining methods are the respective modifiers for the “$name”, “$action” and “$method” properties. They look like this:

// set form name
public function setName($name){
    $this->name=$name;
}
// set form action
public function setAction($action){
    $this->action=$action;
}
// set form method
public function setMethod($method){
    if($method!=’post’&&$method!=’get’){
        throw new Exception(‘Invalid form method’);
    }
    $this->method=$method;
}

Because of their extreme simplicity, the above listed methods shouldn’t be difficult to understand, since they’re tasked with setting the values for each form property I mentioned before. Probably, the “setAction()” method might be boosted by checking whether the “$action” parameter is a valid file, but additional features will be left as possible class improvements.

Finally, the last method to be listed is “getFormCode()”, which simply returns the complete form’s output, to be displayed accordingly:

// get dynamic form output
public function getFormCode(){
    return $this->output;
}

With all of the methods covered in detail, the next thing to do is set up a practical example, in order to test the capabilities of the recently developed “formGenerator” class and see the real power of design patterns.

{mospagebreak title=A practical example: putting the “formGenerator” class to the test}

Despite the rather complicated-sounding nature of the phrase, building object-based forms is a really easy thing to do. With all of the form element classes already defined, as well as the “formElementFactory” class acting as a real factory that returns form objects, setting up the “formGenerator” class for displaying layout-controlled web forms is a pretty straightforward task.

To test the functionality of the “formGenerator” class, I’ll show an example that builds a simple login form, comprised of “username” and “password” fields, together with the corresponding login button. Also, I’ll add some additional markup within the form, to demonstrate the control over the general layout that the class offers. So, the sample code looks like this:

// define parameters for the formGenerator class
$formElements=array(‘textinput’=>array
(‘name’=>’username’,’maxlength’=>’20’),’passwordinput’=>array
(‘name’=>’password’,’maxlength’=>’20’),’submitbutton’=>array
(‘name’=>’login’,’value’=>’Log in’));
// instantiate a new formGenerator object
$fg=new formGenerator($formElements);
// add element labels
$fg->addFormPart(‘<table style=”float:left;”><tr><td>User
Name<td></tr><tr><td>Password<td></tr></table>’);
// add a table to the form code
$fg->addFormPart(‘<table>’);
// add a table row as element header
$fg->addElementHeader(‘<tr><td>’);
// add closing tags
$fg->addElementFooter(‘</td></tr>’);
// generate form
$fg->createForm();
// add a closing </table> tag
$fg->addFormPart(‘</table>’);
// display the form
echo $fg->getFormCode();

As you can see, although the above snippet takes up only a few lines of code, it is actually very powerful. First, the script builds a recursive array with the name and parameters of each form object to be created. Here is the line responsible for doing this:

// define parameters for the formGenerator class
$formElements=array(‘textinput’=>array
(‘name’=>’username’,’maxlength’=>’20’),’passwordinput’=>array
(‘name’=>’password’,’maxlength’=>’20’),’submitbutton’=>array
(‘name’=>’login’,’value’=>’Log in’));

Then, a new “formGenerator” object is instantiated and the above array is passed to the constructor. Let’s stop briefly at this point and see what’s happening here: if we return to the definition of the class, it’s clear that only two lines are required to generate the main source code of the form, because all of the internal processing for factoring form objects is handled inside the class context. This simple concept demonstrates the real power of the factory pattern, when correctly applied.

The rest of the code uses the “addFormPart()” method to roughly build a table containing the labels for each field, along with the “addElementHeader()” and “addElementFooter()” methods to wrap up form fields into a containing <table> structure.

Finally, the complete (X)HTML markup is displayed by calling the “getFormCode()” method, like this:

echo $fg->getFormCode();

By this point, you’ve been provided with the required background to use some design patterns in order to build web forms based on an object-oriented method. Definitely, repetitive and boring tasks involved in the development of web programs such as form generation, can be easily turned into flexible processes by conceiving them as an issue feasible to be solved through the object-oriented paradigm.

To wrap up, for those developers who want to have available “in one place” all the source code developed for PHP5, I’ll offer a list of each class used to build object-based forms.

{mospagebreak title=The source code box: listing the full developed classes}

As I said before, here is the full list of the classes used over the series:

/*
***************************************
class definitions for form elements
***************************************
*/
// class textinput
class textinput{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”text” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class passwordinput
class passwordinput{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”password” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class hiddeninput
class hiddeninput{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”hidden” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class fileinput
class fileinput{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”file” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class imageinput
class imageinput{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”image” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class radiobutton
class radiobutton{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”radio” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class checkbox
class checkbox{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”checkbox” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class button
class button{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”button” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class submitbutton
class submitbutton{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”submit” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class resetbutton
class resetbutton{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<input type=”reset” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class textarea
class textarea{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<textarea ‘;
        $textvalue=”;
        foreach($attributes as $attribute=>$value){
            ($attribute!=’value’)?$this-
>html.=$attribute.’=”‘.$value.'” ‘:$textvalue=$value;
        }
        $this->html=preg_replace(“/”? $/”,””>”,$this->html);
        $this->html.=$textvalue.'</textarea>';
    }
    public function getHTML(){
        return $this->html;
    }
}
// class selectbox
class selectbox{
    private $html;
    public function __construct($attributes=array()){
        if(count($attributes)<1){
            throw new Exception (‘Invalid number of attributes’);
        }
        $this->html='<select ‘;
        $options=”;
        foreach($attributes as $attribute=>$value){
            if($attribute!=’options’){
                $this->html.=$attribute.’=”‘.$value.'” ‘;
            }
            else{
                foreach($value as $values=>$label){
                    $options.='<option value=”‘.$values.'”>’.$label.'</option>';
                }
            }
        }
        $this->html=preg_replace(“/”? $/”,””>”,$this->html);
        $this->html.=$options.'</select>';
    }
    public function getHTML(){
        return $this->html;
    }
}
/*
*****************************************
abstract formElementFactory class
*****************************************
*/
// abstract class formElementFactory
// this class is abstract thus cannot be instantiated
abstract class formElementFactory{
    private function __construct(){}
    public static function createElement($type,$attributes=array
()){
        if(!class_exists($type)||!is_array($attributes)){
            throw new Exception(‘Invalid method parameters’);
        }
        // instantiate a new form element object
        $formElement=new $type($attributes);
        // return HTML of form element code
        return $formElement->getHTML();
    }
}
/*
**************************************
// formGenerator class definition
**************************************
*/
class formGenerator{
    // data member declaration
    private $elements=array(); // array of form elements
    private $output=”; // dynamic output
    private $elementHeader=”; // element header
    private $elementFooter='<br />'; // element footer
    private $name=’theform'; // form name
    private $method=’post'; // form method
    private $action; // form action
    // constructor
    public function __construct($elements=array()){
        if(count($elements)<1){
            throw new Exception(‘Invalid number of elements’);
        }
        // data member initialization
        $this->elements=$elements;
        $this->action=$_SERVER['PHP_SELF'];
    }
    // create form code
    public function createForm(){
        $this->output.='<form name=”‘.$this->name.'”
action=”‘.$this->action.'” method=”‘.$this->method.'”>';
        foreach($this->elements as $element=>$attributes){
            // call the abstract class formElementFactory
            $this->output.=$this-
>elementHeader.formElementFactory::createElement
($element,$attributes).$this->elementFooter;
        }
        $this->output.='</form>';
    }
    // add form part
    public function addFormPart($html='<br />’){
        $this->output.=$html;
    }
    // add element header
    public function addElementHeader($header){
        $this->elementHeader=$header;
    }
    // add element footer
    public function addElementFooter($footer){
        $this->elementFooter=$footer;
    }
    // set form name
    public function setName($name){
        $this->name=$name;
    }
    // set form action
    public function setAction($action){
        $this->action=$action;
    }
    // set form method
    public function setMethod($method){
        if($method!=’post’&&$method!=’get’){
            throw new Exception(‘Invalid form method’);
        }
        $this->method=$method;
    }
    // get dynamic form output
    public function getFormCode(){
        return $this->output;
    }
}

That’s about it. Hopefully you’ll have some fun studying the code and tweaking it to fit your personal needs. Also, so as to not make the code excessively long, I’ve not listed the classes suitable for PHP 4, but you may want to take a look at them in the previous article. A final note: all of the examples have been tested on PHP 4.3.4 and PHP 5.0.4. respectively. Happy coding!

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan