Creating components using Delphi for PHP

By: José León Serna

Abstract: Features how to create simple VCL for PHP components

Delphi for PHP provides, through VCL for PHP, a standard to create PHP components that can be integrated inside a visual IDE. This standard has been checked to be as open as possible, so you can reuse existing PHP code base.

By creating components, you get a lot of benefits:

  • Abstract and wrap working functionality and provide an easy to use interface through properties, methods and events
  • Provide maximum reusability
  • Get the best visual development experience
  • And allow third parties to easily use your code

On a previous article, we saw how easy is to create graphs with little HTML and CSS so let’s wrap that code into a component you can install on the Tool Palette, drag to your forms and setup its properties using Object Inspector.

If you have developed any Delphi for Windows components, you will see how similar the process is.

    Folder structure

Our components must live inside the VCL for PHP folder, so let’s create a folder inside C:\Program Files\CodeGear\Delphi for PHP\2.0\vcl, called csschart and create a unit named csscharts.inc.php, that’s the unit were the code for our new component is going to be written.

    Inheriting

To start development, we can inherit from an existing class, for our example, the most appropriate class to inherit from is Control, is the right class if we want to create a visual control which doesn’t allow controls to be dragged inside, but a Control can be placed inside a container.

Here is the basic code for the control:

<?php
use_unit("controls.inc.php");

class CSSChart extends Control
{
  function __construct($aowner = null)
  {
    parent::__construct($aowner);
  }
}
?>

The component class is named CSSChart, inherits from Control, and overrides the constructor, because we are going to use it later.

    Adding a property

We are going to need a property to store the data we want to show on the chart, so let’s add it, we can use the Edit | Add published property option, which requests the name for the property and default value, this is the code generated:

protected $_data = array();
function getData() { return $this->_data; }
function setData($value) { $this->_data = $value; }
function defaultData() { return array(); }

The protected field that effectively stores the information, the getter , the setter and default functions. This property, because starts with ‘get‘, it’s shown on the Object Inspector, and the user will be able to modify it.

    Initializing

Now we need to add some initialization code, for example, to set the default control dimensions and initialize the data array so it matches with the default value.

This is how the constructor should look now:

function __construct($aowner = null)
{
  parent::__construct($aowner);

  //Set the initial properties on the constructor
  $this->Width = 330;
  $this->Height = 145;
  $this->_data = array();
}

    What is going to show the component?

On the previous article there were different types of charts, so let’s pickup, for example, the Horizontal bar chart, and if we take a look at the HTML code used to draw the chart, we get this:

<ul class="chartlist">
<li>
<a href="http://www.example.com/fruits/apples/">Apples</a>
<span class="count">420</span>
<span class="index" style="width: 42%">(42%)</span>

</li>
<li>
<a href="http://www.example.com/fruits/bananas/">Bananas</a>
<span class="count">280</span>
<span class="index" style="width: 28%">(28%)</span>
</li>
<li>

<a href="http://www.example.com/fruits/cherries/">Cherries</a>
<span class="count">200</span>

<span class="index" style="width: 20%">(20%)</span>
</li>
<li>
<a href="http://www.example.com/fruits/dates/">Dates</a>

<span class="count">100</span>
<span class="index" style="width: 10%">(10%)</span>
</li>
</ul>

Which is a basic list and each item has a text with two spans, so, the basis for VCL for PHP component development is to turn that, in dynamic code, using the information provided by component properties.

To tell the component to show something on the browser, we need to override the dumpContents() method, this method is the one called to generate component contents, so let’s use the Data property we added before, to generate the HTML code.

function dumpContents()
{
  //Get the data to use
  $data = $this->_data;

  //Echoes the start of the list
  echo '<ul class="chartlist">';

  //Iterates through the data and dump each <li> tag
  reset($data);
  while(list($key, $val) = each($data))
  {
    echo '<li>';
    echo '<A href="">';
    echo $key;
    echo '</a>';
    echo '<span class="count">' . $val . '</span>';
    echo '<span class="index" style="width: ' . ($val / 10) . '%">(' . ($val / 10) . '%)</span>';
    echo '</li>';
  }

  //Echoes the end of the list
  echo '</ul>';
}

This code iterates through the data to show, and dumps out the code required depending on each item of the data array.

    Default data

If we install this component as it is right now, and we drop it into a Page, the Data property is empty and we will see nothing, so there is something we can do about that, if we are in design-time.

Let’s rewrite the dumpContents() method and check if we have information to show or not. If there is no information and we are inside the IDE, let’s add some sample data, so the user can check how the control will look.

function dumpContents()
{
  //Get the data to use
  $data = $this->_data;

  //If there is no information to show
  if(count($data) == 0)
  {
    //And we are in design-time
    if(($this->ControlState & csDesigning) == csDesigning)
    {
      //Let's use some sample data
      $data = array();
      $data['PHP'] = 800;
      $data['Delphi'] = 700;
      $data['Java'] = 500;
      $data['C#'] = 400;
    }
  }

  //Echoes the start of the list
  echo '<ul class="chartlist">';

  //Iterates through the data and dump each <li> tag
  reset($data);
  while(list($key, $val) = each($data))
  {
    echo '<li>';
    echo '<A href="">';
    echo $key;
    echo '</a>';
    echo '<span class="count">' . $val . '</span>';
    echo '<span class="index" style="width: ' . ($val / 10) . '%">(' . ($val / 10) . '%)</span>';
    echo '</li>';
  }

  //Echoes the end of the list
  echo '</ul>';
}

We can check if we are inside the IDE or not using the ControlState property and the csDesigning style, which is set only when the component is working inside the IDE.

    Styling the lists

If we run that component, we will see the information is there, but it doesn’t looks good, or, at least, as it looks on the original article, what is missing? Charts are drawn using styles, so we will need to make our component to dump out style information.

The first step is to create a .css file with all the styles we need to use, let’s name it csscharts.css and place it in the same folder csscharts.inc.php is.

Add this code:

/* CHART LISTS */
.chartlist {
font-family: Helvetica, Arial, sans-serif;
color: #333;

float: left;
border-top: 1px solid #EEE;
width: 15em;
}
.chartlist li {
position: relative;
display: block;
border-bottom: 1px solid #EEE;
_zoom: 1;
}
.chartlist li a {

color: #2D7BB2;
text-decoration: none;
font-weight: bold;

display: block;
padding: 0.4em 4.5em 0.4em 0.5em;
position: relative;
z-index: 2;
}

.chartlist li a:hover {
color: #333;
}

.chartlist .count {
display: block;
position: absolute;
top: 0;
right: 0;
margin: 0 0.3em;
text-align: right;
color: #999;
font-weight: bold;
font-size: 0.875em;
line-height: 2em;
}
.chartlist .index {
display: block;
position: absolute;
top: 0;
left: 0;
height: 100%;
background: #B8E4F5;
text-indent: -9999px;
overflow: hidden;
line-height: 2em;
}
.chartlist li:hover {
background: #EFEFEF;
}

And now the CSS style sheet is ready, we need to tell the component to use it, that can be done by overwritting dumpHeaderCode(), which is called when the Page component is generating the <head> section of the document, the right place to dump style sheets.

function dumpHeaderCode()
{
  parent::dumpHeaderCode();
  echo '<link type="text/css" rel="StyleSheet" href="'. VCL_HTTP_PATH .'/csschart/csscharts.css" />';
}

We call the dumpHeaderCode() method of the parent class, and also dump out a line to include the .css we have just written.

    What is VCL_HTTP_PATH?

Is a constant defined dynamically by the VCL and using it, we can be sure that it refers to the VCL folder, as seen by the browser, so it’s an easy way to include resources (images, JavaScript, CSS) in our component without having to take care of such things.

    Multiple components

But, what happen if I include five chart components in my page? The .css style sheet is going to be included five times!!!, so an easy way to prevent this from happen is to make a check if we have added that before.

function dumpHeaderCode()
{
parent::dumpHeaderCode();

//If we have not dump the style sheet for charts yet, let's do it
if(!defined('CSSCHARTS'))
{
define('CSSCHARTS', 1);
//Note the VCL_HTTP_PATH constant
echo '<link type="text/css" rel="StyleSheet" href="'. VCL_HTTP_PATH .'/csschart/csscharts.css" />';
}
}

And this is it, the component is ready to be added to the IDE. We could have added it before, but it’s better we work on a sample project and where the component is created in runtime, so we can test as we are developing. Once the component is almost ready, we can add it safely to the IDE.

    Adding the component to the IDE

To do that, a package is required, create a new unit and name it csscharts.package.php, by saving it in the same folder we are working on.

Here is the code for the package which installs this component:

<?php
require_once("vcl/vcl.inc.php");
use_unit("designide.inc.php");

setPackageTitle("CSS Charts sample component");
//Change this setting to the path where the icons for the components reside
setIconPath("./icons");

//Change yourunit.inc.php to the php file which contains the component code
registerComponents("CSSChart", array("CSSChart"), "csschart/csscharts.inc.php");

registerPropertyEditor("CSSChart","Data","TValueListPropertyEditor","native");
?>

The unit designide.inc.php contains the functions we are going to use:

  • setPackageTitle tells the IDE the title of the package to be shown
  • setIconPath tells the IDE where to find icons for components on this package
  • registerComponents tells the IDE to install, on the palette CSSChart, a component named CSSChart, which is stored on the csschart/csscharts.inc.php unit. The path must be relative to the VCL for PHP path.
  • registerPropertyEditor tells the IDE to use the property editor named TValueListPropertyEditor to edit the Data property of the CSSChart component

And now, using Component | Install Packages, our component is at the bottom of the Tool Palette, let’s drop it inside a Page, and this is what we get:

Hide image
Click to see full-sized image

If you select the Data property, you will see an ellipsis button waiting for you to click, if you do it, you will be allowed to customize the Data property array with a dialog. This is what you get if you modify that property in design-time.

Hide image
Click to see full-sized image

So, as you can see, it’s very easy to create a VCL for PHP component and there is a lot of similarities with creating Delphi for Windows components.


On a future article, we will see how to add a DataSource property to this component, so the data comes from a database table, making this component a lot more useful.

Download the code for this component

Server Response from: ETNASC03