5.4. New components

In this tutorial we will show, how to write an automated component for the input fields. Its features:

  1. The component will check, whether the form validation is OK.
  2. If not, it will automatically get the previously written data from the $_POST array.
  3. In case of error, it will automatically get and print the message for the user.

Such automated component will be useful while form building. You can easily handle the invalid data events by refreshing the form. The components will do everything else, and moreover - the templates will be clear. The requested information will be retrieved from the template blocks, which we will pass to the parser using the assign() method.

The component creation consists in implementing the ioptComponent interface and registering the new component in the library. Create a directory somewhere in your project tree, and put there a file named component.myInputComponent.php.

<?php
 
	class myInputComponent implements ioptComponent
	{
		private $name;
		private $validForm = true;
		private $predefinedValues = array();
		private $messageBuffer = array();
		private $htmlTags = array();
		private $opt;

We define here some class fields:

  1. $name - remembers the input field name.
  2. $validForm - the information whether the form is correctly validated..
  3. $predefinedValues - a reference to the predefined form vield value list.
  4. $messageBuffer - a reference to the error message list.
  5. $htmlTags - the array of the HTML attributes defined for the input tag.
  6. $opt - an optClass instance.

The data for the first four fields are read directly from the template engine. The last two ones are the internal class fields. Let's write a class constructor:

		public function __construct($name = '')
		{
			if($name != '')
			{
				$this -> htmlTags['name'] = $this -> name = $name;
			}
		} // end __construct();

In case of creating components on the script side, we allow to define their name in the constructor, which is comfortable for the programmer. The next method, setOptInstance() is a part of the component interface. It gets the optClass object and reads the necessary data from the parser.

		public function setOptInstance(optClass $tpl)
		{
			$this -> tpl = $tpl;
			
			if(isset($this -> tpl -> data['validForm']))
			{
				$this -> validForm =
					(bool)$this -> tpl -> data['validForm'];
			}
			if(isset($this -> tpl -> data['messageBuffer']))
			{
				$this -> messageBuffer =
					&$this -> tpl -> data['messageBuffer'];
			}
			if(isset($this -> tpl -> data['predefinedValues']))
			{
				$this -> predefinedValues =
					&$this -> tpl -> data['predefinedValues'];
			}
		} // end setOptInstance();

To receive the data, we use the object we have already got. They are stored in the block memory: optClass::$data assotiative array.

		public function set($name, $value)
		{
			$this -> htmlTags[$name] = $value;
			if($name == 'name')
			{
				$this -> name = $value;
			}
		} // end set();

The method above helps us to set the component parameter values. By default, we treat them as HTML attributes, however, in case of the parameter named "name", we have to change additional class field.

		public function push($name, $value, $selected = false)
		{		
		} // end push();
 
		public function setDatasource(&$source)
		{
			$this -> predefinedValues = $source;
		} // end setDatasource(); 

Our component does not support the push() method, which is used to handle the listItem component tags. Let's take a look at the next method. As you might have noticed, the component normally loads the predefined value list automatically in setOptInstance(). Here we allow the programmer to define manually the datasource, which is treated as a new predefined value list. It may be changed with the datasource component parameter.

		public function begin()
		{
			if(!$this -> validForm)
			{
				$this -> htmlTags['value'] = $_POST[$this -> name];
			}
			else
			{
				$this -> htmlTags['value'] = '';
				if(isset($this -> predefinedValues[$this->name]))
				{
					$this -> htmlTags['value'] =
						$this -> predefinedValues[$this->name];
				}
			}
			
			echo '<input type="text" '.
				generateTagElementList($this->htmlTags).'/>';
		} // end begin();
 
		public function end()
		{		
		} // end end(); 

Now, it is time for the heart of the component - HTML code generation. We will stop here for a minute to describe the process. There are two methods that perform the task - begin() and end(). Between them, OPT runs the component events with the "middle" position attribute. In our case, there is absolutely nothing to do in end(), so we simply make "middle" be identical to position "down".

The most of the begin() code concerns the default value receiving. If the form is not valid, we read the value that the user has already typed in order not to do this again, when the form is refreshed. In the other case, we first set the null value and check, if the script has got some predefined ones. If yes, we assign them to the component. Note that the HTML code is generated by simple echo command. To link the attribute array into a tag, we use the function generateTagElementList() available in OPT.

		public function onMessage($msg)
		{
			if(!$this -> validForm)
			{
				if(isset($this -> messageBuffer[$this->name]))
				{
					$this -> tpl -> vars[$msg] =
						$this -> messageBuffer[$this->name];
					return true;
				}
			}
			return false;
		} // end onMessage();
 
	}
 
?>

The last thing to do is to write some events. Our component has only one, which handles the situation, when the field is incorrectly filled in and we should display a message, what is wrong. The event method works in this way: it checks, whether the event has taken place. In this case, it returns true and sets a template variable with the name provided in the $msg variable. In our example, we set there the error message.

The event activation makes the assigned HTML code displayed, which obviously means that the user can see the error message.

Now some practical examples, how to use our newly created component. You can register it manually or include as a plugin. For more details, see OPT Plugins. First, we will show a template with the component.

<html>
<head>
  <title>Open Power Template: components</title>
</head>
<body>
<opt:bindEvent id="defaultMessage" name="onMessage"
	message="msg" position="down">
<br/><span style="color: #ff0000;">{@msg}</span>
</opt:bindEvent>
 
<opt:if test="not $validForm">
<p>The form was incorrectly filled in.</p>
</opt:if>
 
<form method="post" action="script.php">
<table border="0" width="50%">
 <opt:section name="components">
 <tr>
   <td width="40%"><strong>{$components.title}</strong></td>
   <td width="60%"><opt:component id="$components.item">
	<opt:load event="defaultMessage"/>
   </opt:component></td>
 </tr>
 </opt:section>
 <tr>
   <td width="40%">&nbsp;</td>
   <td width="60%"><input type="submit" value="Send"/></td>
 </tr>
</table>
</form>
</body>
</html>

The form is fully dynamic - the components are created in the script and sent to the template in a section. We provided there only an undefined component, where we can port the objects of our class. Note also that we created the event outside the component - it can be now placed in a master template and used in other forms, so that the code is much shorter and clearer.

Here we can see the power of the components - the logic of form processing, such as choosing the predefined value or how to handle the error messages, is hidden. We only point a place, where we want to have everything and provide a HTML code for the message.

To run the example, we need one more template, to print the results.

<html>
<head>
  <title>Open Power Template: components</title>
</head>
<body>
<table border="0" width="50%">
 <tr>
   <td width="40%">Name:</td>
   <td width="60%">{$name}</td>
 </tr>
 <tr>
   <td width="40%">Surname:</td>
   <td width="60%">{$surname}</td>
 </tr>
  <tr>
   <td width="40%">E-mail:</td>
   <td width="60%">{$email}</td>
 </tr>
</table>
</body>
</html>

And now it is time for the final cut - the PHP script.

<?php
	define('OPT_DIR', './lib/');
	require('./lib/opt.class.php');
 
	try
	{	
		$tpl = new optClass;
		$tpl -> root = './templates/';
		$tpl -> compile = './templates_c/';
		$tpl -> plugins = './plugins/';
		$tpl -> gzipCompression = true;
		$tpl -> xmlsyntaxMode = true;
		$tpl -> httpHeaders(OPT_HTML);
		$tpl -> loadPlugins();

This is a simple OPT initialization. In addition to the standard procedure, we also load our component plugin.

		$components = array();
		$components[0] = array(
			'title' => 'Name',
			'item' => new myInputComponent('name')
		);
		$components[1] = array(
			'title' => 'Surname',
			'item' => new myInputComponent('surname')
		);
		$components[2] = array(
			'title' => 'E-mail address',
			'item' => new myInputComponent('email')
		);

The script creates here an array for the section that contains all the form components. We create them as normal class objects with the description and HTML name defined.

		if($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$valid = 1;
			$messages = array();
			if(strlen($_POST['name']) < 3)
			{
				$valid = 0;
				$messages['name'] = 'Specified name is too short!';
			}
			if(strlen($_POST['surname']) < 3)
			{
				$valid = 0;
				$messages['surname'] = 'Specified surname is too short!';
			}
			if(!preg_match('/(.+)\@(.+)\.(.+)/',
			$_POST['email']))
			{
				$valid = 0;
				$messages['email'] = 'Specified e-mail address is invalid.';
			}

Here we start the form validation. By default, we assume that the form is correct. Only if we notice that something is wrong with a field, we change the state and provide the error message to display.

			if(!$valid)
			{
				$tpl -> assign('validForm', false);
				$tpl -> assign('messageBuffer', $messages);
				$tpl -> assign('components', $components);
				$tpl -> parse('form.tpl');
			}
			else
			{
				$tpl -> assign('name', $_POST['name']);
				$tpl -> assign('surname', $_POST['surname']);
				$tpl -> assign('email', $_POST['email']);
				$tpl -> parse('results.tpl');
			}

The next part of the form processing. If the data are correct, we display the results in the browser, or do everything else with them. In the other case, the form is begin refreshed. We have to assign the information that something went wrong, the error messages and the component list, but nothing else. The rest of the login, including the previously written values, is hidden in the component.

		}
		else
		{
			$tpl -> assign('validForm', true);
			$tpl -> assign('components', $components);
			$tpl -> parse('form.tpl');
		} 
	}
	catch(optException $exception)
	{ 
		optErrorHandler($exception); 
	}
?>

The last lines of the script handle the situation, when the form is displayed for the first time. We have to tell it by setting the validForm block to true and provide the component array.

This is the end of the component tutorial. We hope you have convinced that components are very nice things both for the programmer and template designer and you will be using it in your projects. We are currently working on the official OPT add-on called "Open Power Forms". It makes a strong use of the components in the form processing. They are connected there with the validation system and are easy to customize, however everything lies on the basic idea presented above.