Overview
OK, so you've heard all about it, you think you've got your head around it.
Everyone you speak to says it's the way real developers develop. How do you do it?
What's in it for you? And how the hell do you do it with PHP?
Broadly speaking, all of the above statements are true, but I guess if you're
not familiar with OO you want to see a concrete example - and ideally something, which
you can fit into your own page(s). Well, here goes.
Let's start off by seeing exactly what we're aiming for.
Basically, we have three boxes (F1 Teams, Drivers Championship and Constructors Championship).
F1 Teams
Quite simply, this is a box with an un-ordered list in it. Each item is a link to the Team's web site.
Drivers Championship
This is an informational box, showing the current points standing in the F1 Drivers Championship.
There are no links within this box.
Constructors Championship
Like the Drivers Championship box, this is also an informational box, showing the current
points standing fo the F1 Constructors Championship.
Now, before you say it, I know you probably don't want details on the current F1 championship,
but this does serve as a sample application. At the end of the day, the data is simply taken from a
database (in this case I am using MySQL), so you can use whatever data you like. The point is these boxes
are "great" portal-type tools for showing lots of focused data. Whether you are a fan of them or not, they
do work.
How do we create them?
In my mind there are a number of ways we can create these:
-
Hard code the data into a web page. There is nothing wrong with this method if you
just want the box(es) to appear on one page, but once you start spreading the across pages, the administrative
overhead does become tedious.
-
Set the boxes up as a server side include file. Again, there is nothing wrong with
this; you can then include the box(es) on any number of pages simply by referencing the include file.
-
Create the boxes as Objects. Initially, this does take a little longer to build,
but it does make for portable code (I'm talking about code which walks from site to site, not just page to
page!). Additionally, we create interfaces to both the data source and the layout of the box(es), which
means we have "easier" control of their data and layout.
As you've probably guessed, we're going with the OO method. What would be the point otherwise?
So, let's start at the top...
Setting the requirements. For the purpose of this exercise, I'm going to define my requirements as:
-
Data source must be variable and controllable.
-
Layout dimensioning must be variable and controllable.
-
Colour must be variable and customisable.
-
Font Face must be controllable.
There are a number of ways we can meet these criteria; and for the latter two, it is probably
easiest to make use of CSS, which is what we'll do. As for the first two, so the story begins:
To build this box we are going to use 7 separate files:
-
index.phtml
-
mysqldb.obj
-
infobox.obj
-
linkbox.obj
-
resultbox.obj
-
constants.inc
-
main.css
Standards
All .obj files are class declarations. I use one file per class/subclass. The .phtml file is
the file we are gong to insert our box(es) into. The .inc file is a generic server side include file
and the CSS file is exactly that - a Style Sheet.
Development Process
As always, we will start by defining our global constants, then we'll move onto create our
data schema (and database), followed by our data class, then our box classes and finally, we'll create
the .phtml file, which will bring it all together into a working example - or at least, that's the plan
at the moment!
Constants
This is a straight forward PHP file, which I create to define Constant values I can use
across a whole site. By using this - appropriately - I can change the look/feel of a site from one
place, rather than having to chase variable values across multiple pages. Here's our constants.inc sample:
<?php
// Database Constants
$HOST = "localhost";
$DB = "testing";
$WEBUSER = "root";
$WEBPASSWORD = "";
// Colour Constants
$COLOR_PRIMARY = "#037B0B";
$COLOR_SECONDARY = "#FFFFC0";
$COLOR_TERTIARY = "#ECED81";
// Value Constants
$TRUE = 1;
$FALSE = 0;
// Application Specific
$TITLE = "Object Orientation Demonstration";
$ADMINEMAIL = "webmaster@domin.com";
// CSS Plug-in Values
$CSSBOXTITLE = "boxtitle";
?>
Database Schema
Under no circumstances should this be taken as a de-facto database schema, but it works for
the purposes of this example:
As you can see, the data for News is in a table in its own right, whilst the race, driver,
team and points details are the result of related tables. Suffice to say, if you do not understand
this schema, you should go and read up on some RDBMS introductory material before going any further.
mysqldb Class
So, now we have defined our RDBMS and we know where our data is stored. It's time to start
playing around with OO!!!!
If you have played around with PHP for any length of time, you will be well aware there are
many functions available for interfacing to a number of RDBMS. Essentially, this class is going to
serve as a "wrapper" to some of the MySQL functions - of course, if you are writing for Postgres
(or Oracle, or any other RDBMS) you can modify these accordingly.
Here is the code:
<?php
class mysqldb {
//set up the object
var $host;
var $db;
var $dbuser;
var $dbpassword;
var $sql;
var $numberrows;
var $dbopenstatus;
var $dbconnection;
/*
Use these functions to get and set the values of this object's
variables. This is good OO practice, as it means that datatype
checking can be completed and errors raised accordingly.
*/
// Property Get & Set
function gethost() {
return $this->dbhost;
}
function sethost($req_host) {
$this->dbhost = $req_host;
}
function getdb() {
return $this->db;
}
function setdb($req_db) {
$this->db = $req_db;
}
function getdbuser() {
return $this->dbuser;
}
function setdbuser($req_user) {
$this->dbuser = $req_user;
}
function getdbpassword() {
return $this->dbpassword;
}
function setdbpassword($req_password) {
$this->dbpassword = $req_password;
}
function getsql() {
return $this->sql;
}
function setsql($req_sql) {
$this->sql = $req_sql;
}
function getnumberrows() {
return $this->numberrows;
}
function setnumberrows($req_numberresults) {
$this->numberesults = $req_numberresults;
}
function setdbconnection($req_dbconnection) {
$this->dbconnection = $req_connection;
}
function getdbconnection() {
return $this->dbconnection;
}
/*
This is the constructor for the object. In this case I have set
the initial values of a number of the object properties to those
values declared in the global constants.inc. By doing this, I
only need to change the values of these properties for specific
operations, which we will not need to do throughout this example
*/
function mysqldb() {
global $HOST, $DB, $WEBUSER, $WEBPASSWORD;
global $TRUE, $FALSE;
$this->sethost($HOST);
$this->setdb($DB);
$this->setdbuser($WEBUSER);
$this->setdbpassword($WEBPASSWORD);
$this->setdbconnection($FALSE);
}
/*
These are the methods for the object. They provide for opening a
connection to the database, closing a connection and executing a
SELECT query. Of course, these can be expanded upon to allow for
INSERT's, UPDATE's and DELETE's etc...
*/
function opendbconnection() {
global $TRUE, $FALSE;
$this->dbconnection = mysql_connect("$this->dbhost", "$this->dbuser", "$this->dbuserpassword");
if ($this->dbconnection == $TRUE) {
$this->db = mysql_select_db("$this->db");
$this->setdbconnection($TRUE);
} else {
$this->setdbconnection($FALSE);
return false;
}
return true;
}
function closedbconnection() {
if ($this->dbconnection = $TRUE) {
mysql_close($this->dbconnection);
}
}
function selectquery() {
global $TRUE, $FALSE;
if ($this->dbconnection == $FALSE) {
$this->opendbconnection();
}
$this->qry = mysql_query($this->sql);
if (!$this->qry) {
return false;
} else {
$this->numberrows = mysql_num_rows($this->qry);
if ($this->numberrows > 0) {
for($x = 0; $x > $this->numberrows; $x++) {
$this->result[$x] = mysql_fetch_row($this->qry);
}
} else {
echo("[Error:] Retrieving data");
return false;
}
return true;
}
}
}
?>
That's it for the data class. Now we need to set up the class's for the actual layout - the boxes!
genericinfo Class
In this particular scenario, we are going to create three classes. That's right, three classes. One for managing the data and two sub-classes for drawing the boxes. Let's start with the managing data class (which we'll call genericinfo). Here's the code:
<?php
class genericinfo {
//Set up the Object, reserving memory space for variables
var $outerwidth;
var $outerbordercolor;
var $outerborderwidth;
var $titlebgcolor;
var $innerwidth;
var $innerbgcolor;
// Textual variables
var $title;
// Style vairables
var $cssboxtitle;
/*
Use these functions to get and set the values of this
object's variables. This is good OO practice, as it means
that datatype checking can be completed and errors raised accordingly.
*/
function setouterwidth($req_outerwidth) {
$this->outerwidth = $req_outerwidth;
}
function getouterwidth() {
return $this->getouterwidth;
}
function setouterbordercolor($req_outerbordercolor) {
$this->outerbordercolor = $req_outerbordercolor;
}
function getouterbordercolor() {
return $this->outerbordercolor;
}
function setouterborderwidth($req_outerborderwidth) {
$this->outerborderwidth = $req_outerborderwidth;
}
function getouterborderwidth() {
return $this->outerborderwidth;
}
function settitlebgcolor($req_titlebgcolor) {
$this->titlebgcolor = $req_titlebgcolor;
}
function gettitlebgcolor() {
return $this->titlebgcolor;
}
function setinnerwidth($req_innerwidth) {
$this->innerwidth = $req_innerwidth;
}
function getinnerwidth() {
return $this->innerwidth;
}
function setinnerbgcolor($req_innerbgcolor) {
$this->innerbgcolor = $req_innerbgcolor;
}
function getinnerbgcolor() {
return $this->innerbgcolor;
}
function settitle($req_title) {
$this->title = $req_title;
}
function gettitle() {
return $this->title;
}
function setcssboxtitle($req_cssboxtitle) {
$this->cssboxtitle = $req_cssboxtitle;
}
function getcssboxtitle() {
return $this->cssboxtitle;
}
/*
This is the constructor for the object. In this case I have set the initial
values of a number of the object properties to those values declared in the
global constants.inc. By doing this, I only need to change the values of
these properties for specific operations, which we will not need to
do throughout this example
*/
function genericinfo() {
global $COLOR_PRIMARY, $COLOR_SECONDARY, $COLOR_TERTIARY;
global $CSSBOXTITLE;
$this->setouterwidth(150);
$this->setouterbordercolor($COLOR_TERTIARY);
$this->setouterborderwidth(1);
$this->settitlebgcolor($COLOR_PRIMARY);
$this->setinnerwidth(146);
$this->setinnerbgcolor($COLOR_SECONDARY);
if (isset($CSSBOXTITLE)) {
$this->setcssboxtitle($CSSBOXTITLE);
}
}
// Methods
}
?>
You will no doubt notice from this that there are no methods for the class. Why? Well, I guess in a
practical situation you might well choose to combine these three objects into one, simply changing the methods
according to the desired box. However, this is a real-life example of using classes and sub-classes
("inheritance") and the reason I have chosen to sub-class is so that as I add more flavours of box (eg,
curved corners), I can create further sub-class(es); increasing the portability of the right code.
linkbox Class
This class will take the data which it is supplied with and then generate the linkbox for us.
Essentially, a multi-part array is parsd to the linkbox. This means the array will consist of each piece of
data, which is made up of two parts - the bit you want the user to see and the bit which is the actual URL.
<?php
class linkbox extends genericinfo {
/*
Set up the Object. You will notice, we have not reserved
memory space for variables. In this circumstance it is not necessary.
*/
/*
This is the constructor for the linkbox. The only thing this does
is to call the constructor of the parent class. Why? Well, whilst
PHP manages a certain part of OO, one of the bits it falls down on
(at the moment) is constructors within sub-classes. So, to
be sure that the sub-class is instantiated with the constructor of
the parent class, I simply call the parent constructor. Of course,
if I then wanted to override any of the values, I could easily do so.
*/
function linkbox() {
$this->genericinfo();
}
/*
This is the only method within the class. Quite simply, as you can see
it draws the table(s), placing the required data in the appropriate place.
*/
function drawlinkbox() {
echo("<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
echo("<TR>");
echo("<TD>");
if (isset($this->cssboxtitle)) {
echo("<DIV CLASS=\"" . $this->getcssboxtitle() . "\">");
echo($this->title);
echo("</DIV>");
} else {
echo($this->title);
}
echo("</TD>");
echo("</TR>");
echo("<TR>");
echo("<TD>");
echo("<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
echo("<TR>");
echo("<TD>");
echo("<UL>");
for ($x = 0; $x < count($this->data); $x++) {
echo("<LI><A HREF=\"" . $this->data[$x][1] . "\">" . $this->data[$x][0] . "</A></LI>");
}
echo("</UL>");
echo("</TD>");
echo("</TR>");
echo("</TABLE>");
echo("</TD>");
echo("</TR>");
echo("</TABLE>");
}
}
?>
resultbox Class
This is not dissimilar to the linkbox class, except there is not HREF link invovled. instead we create a two-column nested table and place the parsed data into it.
<?php
class resultbox extends genericinfo {
/*
Set up the Object. You will notice, we have not reserved memory
space for variables. In this circumstance it is not necessary.
*/
//constructor
function resultbox() {
$this->genericinfo();
}
/*
This is the only method within the class. Quite simply, as you can see
it draws the table(s), placing the required data in the appropriate place.
*/
function drawresultbox() {
echo("<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
echo("<TR>");
echo("<TD>");
if (isset($this->cssboxtitle)) {
echo("<DIV CLASS=\"" . $this->getcssboxtitle() . "\">");
echo($this->title);
echo("</DIV>");
} else {
echo($this->title);
}
echo("</TD>");
echo("</TR>");
echo("<TR>");
echo("<TD>");
echo("<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
for ($x = 0; $x < count($this->data); $x++) {
echo("<TR>");
echo("<TD>");
echo($this->data[$x][0]);
echo("</TD>");
echo("<TD>");
echo($this->data[$x][1]);
echo("</TD>");
echo("</TR>");
}
echo("</TABLE>");
echo("</TD>");
echo("</TR>");
echo("</TABLE>");
}
}
?>
Index.phtml
How do all these fit together? Well, this is probably best shown by building the page, which we are going to use to present the content to our users, namely index.phtml.
1: <?
2: include "constants.inc";
3: include "mysqldb.obj";
4: include "genericinfo.obj";
5: include "linkbox.obj";
6: include "resultbox.obj"
7: ?>
8: <HTML>
9: <HEAD>
10: <TITLE>
11: <? echo($TITLE); ?>
12: </TITLE>
13: <LINK TYPE="text/css" REL="stylesheet" HREF="main.css">
14: </HEAD>
15:
16: <BODY BGCOLOR="#FFFFFF">
17:
18: <TABLE BORDER="0" CELLPADDING="10" CELLSPACING="10">
19: <TR VALIGN="top">
20: <TD>
21: <?
22:
23: $db0 = new mysqldb();
24: $db0->setsql("SELECT tem_team, tem_url FROM team ORDER BY tem_id");
25: if ($db0->selectquery()) {
26: $lnk = new linkbox();
27: $lnk->settitle("F1 Teams");
28: $lnk->data = $db0->result;
29: $lnk->drawlinkbox();
30: } else {
31: echo("[Error:] Unable to connect");
32: }
33:
34: ?>
35: </TD>
36: <TD>
37: <?
38:
39: $db1 = new mysqldb();
40: $db1->setsql(" SELECT
41: CONCAT(\"<B>\",UPPER(driver.drv_surname), \" \", driver.drv_forename),
42: SUM(points.pts_teampoints) as totdriverpoints
43: FROM points
44: LEFT JOIN driver ON points.drv_id = driver.drv_id
45: GROUP BY driver.drv_surname, driver.drv_forename
46: HAVING totdriverpoints <> 0
47: ORDER BY totdriverpoints DESC");
48:
49: if ($db1->selectquery()) {
50: $rst = new resultbox();
51: $rst->setouterwidth(175);
52: $rst->setinnerwidth(171);
53: $rst->settitle("F1 Drivers Championship");
54: $rst->data = $db1->result;
55: $rst->drawresultbox();
56: } else {
57: echo("[Error:] Unable to connect");
58: }
59:
60: ?>
61: </TD>
62: <TD>
63: <?
64:
65: $db2 = new mysqldb();
66: $db2->setsql(" SELECT
67: team.tem_team,
68: SUM(points.pts_teampoints) as totteampoints
69: FROM points
70: LEFT JOIN team ON points.tem_id = team.tem_id
71: GROUP BY team.tem_team
72: HAVING totteampoints > 0
73: ORDER BY totteampoints DESC");
74:
75: if ($db2->selectquery()) {
76: $rst = new resultbox();
77: $rst->setouterwidth(175);
78: $rst->setinnerwidth(171);
79: $rst->settitle("F1 Constructor's Championship");
80: $rst->data = $db2->result;
81: $rst->drawresultbox();
82: } else {
83: echo("[Error:] Unable to connect");
84: }
85:
86: ?>
87: </TD>
88: </TR>
89: </TABLE>
90: </BODY>
91: </HTML>
Explanation:
Lines 1 - 7 These lines bring in all the files we have previously written, thereby making their contents available. Notice, how the constants.inc file is the first to be included. If it's not, it's not going to be global!
Lines 8 - 20 Here we break out into good ol' HTML, to setup the page and bring in the style sheet.
Lines 21 - 34 Back into PHP to start using the objects.
The first item we are going to create is the F1 Teams box.
Line 23 Instantiates the mysqldb class, creating an object of type mysqldb; to be referenced by $db0. This will therefore execute the constructor function in the mysqldb class, setting initial values to those set by the global constants in constants.inc.
Line 24 Calls the setsql($req_sql) function to set the sql property on the $db0 object.
Line 25 Is an interesting line. Basically, if the call to selectquery() within the mysqldb object fails (or returns as false), execution skips to line 30.
Assuming line 25 returns true, we have the data! Not so bad eh? Now let's work with drawing the box.
Line 26 instantiates the linkbox() class creating an object of type linkbox to be referenced by $lnk. This will therefore execute the constructor function of the linkbox class, which in turn executes the constructor function of the parent class (genericinfo).
Line 27 sets the title of the linkbox, by calling the settitle($req_title) of the parent class.
Line 28 makes sure that the data available to the $lnk is the same as that which is currently in the $db0 object.
Line 29 calls the drawlinkbox() function of the linkbox class. Because $lnk now has direct access to all of the data, it is able to complete the drawing of the linkbox.One point to note in the drawlinkbox function is on lines 16 - 22. If the global constant $CSSBOXTITLE is set, then the style will be included, otherwise it won't.
That's it! You now have the News box drawn on screen!
Lines 35 - 36 Back into HTML to close the table cell and open a new one.
At this point, you should be able to walk through the remainder of the page to see how the other objects
are drawn.
main.css
Finally, here is the Style Sheet, which will be used throughout! Not much more to say on this ;-)
1: <STYLE>
2: .bugresolver {
3:
4: }
5: TD {
6: font-family: verdana, arial, courier;
7: font-size: 10;
8: }
9:
10: P {
11: font-family: verdana, arial, courier;
12: font-size: 20;
13: }
14:
15: A {
16: font-family: verdana, arial, courier;
17: font-size: 12;
18: color: #000084;
19: text-decoration: none;
20: font-weight: bold;
21: text-align: right;
22: }
23:
24: A:hover {
25: font-family: verdana, arial, courier;
26: font-size: 12;
27: color: #990000;
28: background-color: #DCDADA;
29: text-decoration: none;
30: font-weight: bold;
31: text-align: right;
32: }
33:
34: UL {
35: margin-left: 25;
36: }
37: .boxtitle {
38: font-family: verdana, arial, courier;
39: font-size: 14;
40: color: #FFFFFF;
41: font-weight: bold;
42: text-align: center;
43: }
44:
45: .lnkBox {
46: font-family: verdana, arial, courier;
47: font-size: 10;
48: color: #000084;
49: margin-left: 5px;
50: text-align: left;
51: }
52: </STYLE>
Example/Download
For those of you who would like to download this example and give it a try for yourself, a ZIP archive is attached.
To Those Of You With OO Expereice
I am well aware there are a number of "holes" in ths code. For example, I have not completed and data validation when setting and returning property values. However, bear in mind the purpose of this exercise is to give people a "real-world" example (which they can follow) of the process and function of OO within PHP.
To Those Of You Without OO Experience
This is a "real-world" example of the process and function of OO within PHP. There are a number of "holes" within the code. That is no to sat the code is incorrect or wrong, but its function is to show you how to work with OO.
If you're looking for further guidance on developing this code into your site, you should revisit many of the get and set functions within these pages and make sure you develop routines to handle the data being parsed; make sure it is of the correct data-type (eg it is numeric when it should be and it is a valid SQL statement when it should be). Bear in mind, the data these classes are working with is from a pre-populated database, where I knew the data was valid etc....
For more examples of where these type of boxes are used, please visit
e-sphere.net,
f1circle.com and
markaw.com.
About the Author
Professinally, Mark Williams, is, at the moment, the Senior Technical Consultant to one of Europe's top 10 Insurance/Assurance companies. he is working on a number of Internet/e-commerce initiatives inlcluding WAP technology, Video Conferencing, Windows 2000, Linux etc....
To relax, mark is a great F1 fan and on every other Sunday (during the season) you will always find him keeping up to date with the latest events.
More information is available from
markaw.com,
e-sphere.net and
f1circle.com.