Rapid Application Development toolkit for building Administrative Web Applications

RADICORE - A Development Infrastructure for PHP

By Tony Marston

2nd August 2003
Amended 30th April 2012

As of 10th April 2006 the software discussed in this article is available as open source and can be downloaded from www.radicore.org

Introduction
- Background
The 3 Tier Architecture
- Data Access layer
- Business layer
- Presentation layer
The Model-View-Controller (MVC) design pattern
- The Model component
- The View component
- The Controller component
Infrastructure Design
- Forms Families
- Structure, Behaviour, Content ...
- ... and Style
Infrastructure Implementation
- Component scripts
- Transaction Pattern (Controller) scripts
- Generic (abstract) Table class
- Database Table (Model) classes
- Validation class
- DML class
- View object
- Screen Structure scripts
- XML files
- XSL Stylesheets
- XSL Transformation process
- HTML output
- CSS files
- AUDIT class
- Workflow Engine
Levels of Reusability
Extending this Infrastructure
References
Amendment History

Introduction

With every programming language I have worked in it has become normal practice, after having developed an initial series of programs, to identify a common structure to which all subsequent programs should be built. This may take some time as it involves a bit of trial and error in playing with the different ways in which a task can be achieved in order to find the methodology that gives the most advantages in the long term. Eventually this development infrastructure/environment should contain the following:-

The list can be extended even further, but that will do as a starting point.

Background

The PHP development environment that I have devised was created by taking a set of sample programs which I had assembled for a previous language and rewriting them in PHP. These sample programs were used as patterns or templates for all other programs in my applications as they represented all the combinations of structure and behaviour that I had encountered. To build a new component all I had to do was identify the right template, then specify which database tables(s) and columns I wished to show on the screen for the new component. Each database table was accessed via its own separate service component which contained all the business rules associated with that table plus the code to communicate with the database. As each user screen accessed each database table by its own service component it meant that business logic was shared and not duplicated. Both user screens and service components used shared code in the form of 'include' files, so it was possible to update the shared library and have the changes automatically picked up by the various components.

From the outset my aim was to produce an environment with the following characteristics:-

What I actually produced is as follows:-

Interestingly enough my decision to have all HTML output generated through XSL transformations instead of directly by PHP code actually paid enormous dividends by producing a great deal more reusable code than I had originally anticipated. I started by creating a script which performed an operation on a database table then wrote a second script to perform the same operation on a different table. I then compared the two scripts to see what was duplicated and could therefore be put into a sharable file, and what was different and which would have to remain in a script of its own. By careful engineering of the code I ended up with the situation where there were basically only two differences:

I ended up with three types of PHP script in my presentation layer:

When building the XSL stylesheets I came across common code which I was able to move into separate files as XSL templates (subroutines). These templates can be accessed by any number of stylesheets using the <xsl:include> command. A later improvement meant that instead of having a separate XSL stylesheet for each screen where the field names and field labels were hard-coded I could use a much smaller number of generic stylesheets and have the list of field names and field labels supplied within the XML file. The type of HTML control to be used for each field is written to the XML file as a series of field attributes, and a standard XSL template uses these attributes to generate the correct HTML code.


The 3 Tier Architecture

Any piece of software can be subdivided into the following areas:

If you put the code which deals with presentation logic (the generation of HTML documents), business logic (the processing of business rules) and data access logic (the generation and execution of DML (SQL) statements) into a single component then what you have is a single tier structure, as shown in Figure 1.

Figure 1 - 1 Tier architecture

infrastructure-01 (5K)

If you split off all the code that handles the communication with the physical database to a separate component then you have a 2 tier architecture, as shown in Figure 2.

Figure 2 - 2 Tier architecture

infrastructure-02 (8K)

If you go one step further and split the presentation logic from the business logic you have a 3 Tier Architecture, as shown in Figure 3. Note that there is no direct communication between the presentation and data access layers - everything must go through the business layer in the middle.

Figure 3 - 3 Tier Architecture

infrastructure-03 (8K)

When this architecture is implemented the benefits will become apparent as more code can be shared instead of being duplicated. Several components in the presentation layer can share the same component in the business layer, and all components in the business layer share the same component in the data access layer. This is shown in Figure 4. Note also that a presentation layer component can access more than one business layer component, and a business layer component can access other business layer components.

Figure 4 - 3 Tier Architecture in operation

infrastructure-04 (8K)

The big advantage of a 3-tier system is that it is possible to change the contents of any one of the tiers/layers without having to make corresponding changes in any of the others. For example:

You should also notice here that the Business object is only responsible for assembling data in response to a request from the Presentation object. The Business object does not know or care what the Presentation object does with that data - it may build it into a compiled form, an HTML page, a PDF document, a CSV file, an XML file in response to a web service request, or whatever.

By having separate layers with different responsibilities this architecture also makes it possible to use different teams of developers to work on each. That means I can need PHP skills for the business layer, SQL skills for the data access layer, and (X)HTML, CSS and XSL skills for the presentation layer. It may be easier to find developers with skills in one of these areas rather than all three.

The environment which I have created is based on the 3-tier architecture, as shown in Figure 5.

Figure 5 - Environment/Infrastructure Overview

Component Script Controller Script Database Table Class Abstract Table Class Validation Class DML Class Screen Structure Script XML file XSL Stylesheet XSL Transformation Process HTML Output CSS File Audit Class Workflow Engine View Object Presentation layer Business layer Data Access layer infrastructure-05 (13K)

The various components in Figure 5 are described as follows:

  1. Component scripts
  2. Transaction Pattern (Controller) scripts
  3. Database Table (Model) classes
  4. Generic (abstract) table class
  5. Validation class
  6. DML class
  7. View Object
  8. Screen Structure scripts
  9. XML files
  10. XSL Stylesheets
  11. XSL Transformation process
  12. HTML output
  13. CSS files
  14. AUDIT class
  15. Workflow Engine

Note that this is proper 3-Tier Architecture, not the pseudo variety as claimed by many who seem to think that the arrangement of web browser, web server and database automatically constitutes a 3 Tier system. It is the construction of the software in the middle that decides whether the system is 1, 2 or 3 Tier. It is only when a system has separate components to deal with the different areas of logic that it can be truly described as 3-tier.

The infrastructure described in this document has the following degrees of separation:-

Data Access layer

This is part of 3 Tier Architecture. It consists of a one or more DML objects which issue the functions to communicate with the physical database(s). There is a separate DML class for each database engine (MySQL, PostgreSQL, Oracle, SQL Server). This is sometimes referred to as a Data Access Object (DAO).

Not only is it possible within the same transaction to access tables in different databases, it is also possible to access tables through different database engines.

This layer also communicates my AUDIT class in order to record all changes made to an application database in a separate 'audit' database so that they can be reviewed using online enquiry screens.

Business layer

This is part of 3 Tier Architecture. It contains a separate table class for each database table or business entity. Each table class is a subclass of a generic table class so that it can inherit as much generic code as possible.

All communication with the physical database is handled by the generic table class through a separate DML class.

Primary data validation is handled by the generic table class through a validation class. Secondary data validation is performed by customised functions within each table class.

This layer also communicates my Workflow Engine in order to determine if a new workflow case needs to be created, and to progress each case through its various stages.

Presentation layer

This is part of 3 Tier Architecture. It contains a separate component script for each transaction or task within the system. This is a simple mechanism which identifies which model, view and controller components to use.

The controller script handles the transaction behaviour.

The database table class (model) identifies the entity or entities which need to be accessed.

The controller performs operations on the model in order to change the state of the model.

The view object extracts data from the model, formats it, usually into HTML, and sends the result back to the client. There will be different view objects for alternative formats such as PDF or CSV.


The Model-View-Controller (MVC) design pattern

It was some time after I had developed this infrastructure that I discovered that it also contained an implementation of the MVC design pattern. This is discussed in more detail in The Model-View-Controller (MVC) Design Pattern for PHP.

Figure 6 - The Model-View-Controller structure

infrastructure-06 (6K)

The Model component

A model is an object representing data or even an activity. In my infrastructure this is implemented as a series of table classes, one for each table in the database, which inherit a large amount of code from the abstract table class. Each class handles the data validation and business rules for a single database table, but note that all communication with the physical database is routed through a separate DML Object, with a separate DML class for each supported DBMS engine.

The View component

A view is some form of visualisation of the state of the model. In my infrastructure I can format and send data to the client in one of three possible formats:

  1. HTML - This component is given two pieces of information: (a) a screen structure script which identifies which XSL stylesheet to use and which data elements go where on the screen, and (b) one or more database table objects which have already been filled with data. This component will then extract all the data from the database table object(s) and write it out to an XML document. Other data, such as the menu buttons, navigation buttons, pagination and scrolling details, action buttons, et cetera, is also added to the XML document, after which it is transformed into HTML output. For each database table there is typically a list view (containing multiple occurrences with data arranged horizontally) and a detail view (containing a single occurrence with data arranged vertically). The same detail view can be used by the ADD, ENQUIRE, UPDATE, DELETE and SEARCH screens.
  2. PDF - This component is given two pieces of information: (a) a report structure script which identifies which data elements go where on the report, and (b) a single database table object which has issued an SQL "select" statement. It will extract the data one row at a time, write it out to the PDF document, then read the next row.
  3. CSV - This component is given a single database table object which has issued an SQL "select" statement. It will extract the data one row at a time, write it out to the CSV document, then read the next row. The very first row of data is preceded by a list of the column names.

The Controller component

A controller offers facilities to change the state of the model. It accepts input from the user and instructs the model to perform actions based on that input, then updates the view to show the results of those actions.

In my infrastructure this is implemented as a series of component scripts which link to one of a series of transaction pattern (controller) scripts


Infrastructure Design

Some of my approaches to infrastructure design are based on experiences which I have had in previous languages. It is encouraging to know that some of my design decisions as just as valid now as they were then. It just goes to show that quality is ageless.

Forms Families

Some designers have the peculiar notion that the complexity of a system is directly proportional to the number of components it contains, therefore they try to pack as many functions as possible into a single component. In order to maintain the contents of a typical database table it is usual to provide the following functionality:

It is possible to put all this functionality into a single component, but the end result is a very large, very complex component. If this approach is duplicated throughout the entire system the end result is a collection of very large, very complex components. In my experience the size of a component is directly proportional to the amount of effort needed to maintain it, so smaller is better.

The alternative approach, one which I first found to be successful when COBOL was my primary language and which was just as successful when I switched to UNIFACE, is to provide each of these facilities in a separate component. This may produce a large number of components, but at least they are small and simple. The arguments for the 'small and simple' approach against the 'large and complex' are explored in more detail in my article Component Design - Large and Complex vs. Small and Simple.

When I read that when designing components for web pages the 'small and simple' approach was preferred over the 'large and complex' this did not pose a problem for me as this has been my design philosophy for 20 years.

Now when I want to write software to maintain the contents of a typical database table I build a 'family' of small components where each one performs just one of the previously mentioned functions. This produces a family of components (sometimes referred to as forms or screens) with the structure shown in Figure 7. Each of these components has its own Transaction Pattern (Controller) script.

Figure 7 - A typical Family of Forms

infrastructure-07 (1K)

In this structure the LIST (parent) component is the only one that is available on a menu button - all the other child components can only be selected from a navigation button within a suitable parent component. In most cases the child component will need the primary key of an occurrence in the parent component before it can load any data on which it is supposed to act. In this case the required occurrence in the parent screen must be marked as selected using the relevant checkbox before the hyperlink or control button for the child component is pressed.

Another difference between these components is that the LIST component shows multiple rows of details, one occurrence per row, whereas the others will show the details for a single occurrence. As the layout for the SEARCH, INSERT, UPDATE, DELETE and ENQUIRE screens is extremely similar I have managed to provide for their construction with a single XSL file. This means that for each database table I only need 2 XSL files, as shown in Figure 8.

Figure 8 - XSL files required for a family of forms

infrastructure-08 (3K)

This is made possible as one of the parameters used in the XSL transformation process is $mode. This is used within the XSL stylesheet to determine if each field can be input/amended by the user or should be display only.

In my original infrastructure each database table required its own version of the list.xsl and detail.xsl files as the table, field names and field labels had to be hard-coded inside them, but I have since enhanced my XSL library so that a small number of generic stylesheets can be used for any number of database tables. This is documented in Reusable XSL Stylesheets and Templates and uses updated versions of my std.list1.xsl and std.detail1.xsl stylesheets which allow the table name(s), field names and field labels to be passed in as part of the XML data.

Structure, Behaviour, Content ...

In my long career as a software engineer I have written countless hundreds of components, and many times I have come across the situation where I have been asked to create a new component which is "just like that one, but which works on this set of data". In this situation it is necessary to identify those parts of the original component which can be reused 'as is' and those parts which have to be altered. In order to do this I break down each component into the following areas:

The trick now is to make different templates or patterns based on a particular combination of structure and behaviour so that when you build a component from a template all you have to do is specify the content. No two languages provide the same method of creating reusable templates, so what works in one particular language may be totally impossible in another. With PHP the method I have devised is to produce scripts in two categories - Screen Structure scripts which identify the content and reusable Transaction Pattern (Controller) scripts which deal with the structure and behaviour.

... and Style

A feature of HTML documents is that the visual presentation of each page can be altered quite easily. By 'visual presentation' I mean any of the following:

Although it is possible to include all style specifications within an HTML document it is not considered to be good practice. The most efficient method is to extract all style specifications and keep them in a separate Cascading Style Sheet (CSS) file or files. In this way it is possible to update the contents of a single CSS file and have that change automatically inherited by all the pages which reference the styles defined within that CSS file. Without the use of a CSS file it would be necessary to update each page individually, which on a large site could be a long and laborious process.

The term 'cascading' means that an HTML document can actually refer to a series of CSS files. These files will be scanned in the order in which they were defined and their contents merged so that a single specification is the result. In my infrastructure I use several CSS files


Infrastructure Implementation

Although this infrastructure appears to be quite complex due to the large number of components, each component is responsible for just a small area and is therefore relatively simple. The trick is to know which components have to be created by the developer, which components have already been written and are available for immediate use, and how they all hang together.

Component scripts

This is item (1) in Figure 5.

There is one of these for each task (user transaction) within the application. Each task has an entry on the MNU_TASK table in the MENU database so that it can be identified on the ROLE-TASK table (access control list) and made to appear on either a menu bar or a navigation bar.

This component is an implementation of the Transaction Script pattern from Martin Fowler's Patterns of Enterprise Application Architecture (PoEAA).

Each script in this category simply specifies the Model and View before handing control over to a particular Controller.

The component script for a transaction is automatically created when that transaction is generated from the Data Dictionary.

Sample scripts for each pattern can be found in the /radicore/default/ directory.

Here is an example of one of my scripts:

<?php
$table_id = "person";                      // identify the Model
$screen   = 'person.detail.screen.inc';    // identify the View
require 'std.enquire1.inc';                // activate the Controller
?>

As you can see this is a simple script whose purpose is to identify the following:

  1. $table_id identifies the Model part of MVC. This is one of the generated database table classes.
  2. $screen identifies the View part of MVC. This is one of the generated screen structure scripts.
  3. include identifies the Controller part of MVC. This is one of the pre-written controller scripts.

Transaction Pattern (Controller) scripts

This is item (2) in Figure 5.

This can also be referred to as a Transaction Controller or a Page Controller.

Each controller script contains the code to perform a particular action on an unspecified database table. For example:

For a full list of my Transaction Patterns please refer to Transaction Patterns for Web Applications.

Each script in this category is based on a particular combination of structure and behaviour. The actual content is identified by the Screen Structure script which is set in the calling Component script. This means that one of these Controller scripts may be called by many different Component scripts, as shown in Figure 9.

Figure 9 - Many Component scripts to one Transaction Pattern (Controller) script

infrastructure-09 (2K)

None of these Controller scripts outputs any HTML output directly. This is done by the view object which creates an XML file containing all the relevant data, which is then transformed into HTML using a separate XSL stylesheet.

Here is an example of one of my scripts:

<?php
// name = std.enquire1.inc

// type = enquire1

// This will display a single selected database occurrence using $where
// (as supplied from the previous screen)

require 'include.general.inc';

// identify mode for xsl file
$mode = 'enquire';

// initialise session
initSession();

// look for a button being pressed
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
   if (isset($_POST['finish']) or (isset($_POST['finish_x']))) {
      // cancel this screen, return to previous screen
      scriptPrevious();
   } // if
} // if

// create a class instance for the main database table
require "classes/$table_id.class.inc";
$dbobject = new $table_id;

$dbobject->sql_select  = &$sql_select;
$dbobject->sql_from    = &$sql_from;
$dbobject->sql_where   = &$sql_where;
$dbobject->sql_groupby = &$sql_groupby;
$dbobject->sql_having  = &$sql_having;
// check that primary key is complete
$dbobject->checkPrimaryKey = TRUE;

// define action buttons
$act_buttons['finish'] = 'FINISH';

// retrieve profile must have been set by previous screen
if (empty($where)) {
   scriptPrevious('Nothing has been selected yet.');
} // if
   
// get data from the database
$fieldarray = $dbobject->getData($where);
   
if ($dbobject->getErrors()) {
   // some sort of error - return to previous script
   scriptPrevious($dbobject->getErrors());
} // if
	
// check number of rows returned
if ($dbobject->getNumRows() < 1) {
   scriptPrevious('Nothing retrieved from the database.');
} // if

// use contents of first row only
$fieldarray = $fieldarray[0];
   
// rebuild selection using primary key of retrieved row
$where = array2where($fieldarray, $dbobject->getPkey());

// get any extra data and merge with $fieldarray
$fieldarray = array_merge($fieldarray, $dbobject->getExtraData($fieldarray));
	
// build list of objects for output to XML data
$xml_objects[]['root'] = &$dbobject;

// build XML document and perform XSL transformation
$view = new radicore_view($screen_structure);
$view->buildXML($xml_objects, $errors, $message);

?>

Please note the following points:

Here is a brief explanation of my user-defined functions which are contained within file include.inc:

You should notice that the above script does not contain any hard-coded database, table or field names, therefore it can be used for any database table within the system. The points to consider are:

I have some controller scripts which work on more than one database object, such as when dealing with a parent-child relationship or a many-to-many relationship, but the principles are exactly the same.

Generic (abstract) table class

This is item (4) in Figure 5.

This is an abstract class which is based on the ideas outlined in Using PHP Objects to access your Database Tables (Part 1) and (Part 2). It is called an 'abstract' class as it does not contain any implementation details and therefore cannot be instantiated into an object. Implementation details for each physical database table are supplied in separate subclasses, and it is only these subclasses that can be instantiated into objects.

This abstract database class contains generic methods and properties which can be used by any database table in the system. These methods and properties are automatically inherited by every subclass. Code which is specific to any particular table must be built into in the subclass for that particular table.

This generic class also contains the following:

Database Table (Model) classes

This is item (3) in Figure 5.

Each database table (or business entity) is represented by its own class which extends (is a subclass of) the generic table class.

This component is an implementation of the Table Module pattern from Martin Fowler's Patterns of Enterprise Application Architecture (PoEAA). It also serves as the Model in the Model-View-Controller design pattern.

Although each table subclass will inherit all the generic code from the superclass, in order to be usable it must also contain information which is specific to the database table to which it relates. This information is as follows:

Note that this class can deal with any number of database rows - I do not have one version to deal with a single row and a second version to deal with a collection of rows.

The class file for each database table does not have to be generated by hand - with the introduction of A Data Dictionary for PHP Applications it is possible to import the table structures directly from the database schema into the data dictionary, then to export those structures into files which can be accessed directly by the application code.

Validation class

This is item (5) in Figure 5.

The generic validation class handles primary validation (sometimes referred to as declarative checking) of all user input. It compares the contents of the input array with the contents of the $fieldspec array to check that the input data for each field conforms to that field's specifications. It puts any error messages in the current object's $errors array.

The input array (usually the $_POST array) is an array of fieldname=fieldvalue pairs.

The $fieldspec array is an array of fieldname=fieldspec pairs. The fieldspec portion is another associative array of keyword=value pairs.

The $errors array is an array of fieldname=errormsg pairs. It can therefore contain error messages for any number of fields.

Primary validation is limited to the following checks:

Secondary validation, such as comparing one field against another, must be defined within the individual table subclass using the empty classes provided in the superclass.

It is also possible to supplement the generic validation with the addition of plug-ins, as described in Extending the Validation class.

DML class

This is item (6) in Figure 5.

This is the only object in the system which carries out any communication with the physical database. It receives requests from the Generic Table class from which it generates the appropriate DML (Data Manipulation Language) or SQL (Structured Query Language) commands. It then executes these commands by calling the relevant API for the database in question.

This component is an implementation of the Gateway pattern from Martin Fowler's Patterns of Enterprise Application Architecture (PoEAA). As it exists in the Data Access layer it can also be referred to as the Data Access Object (DAO).

There is a separate class for each database engine as each engine has its own set of APIs. This design also allows me to isolate and deal with any differences between the various engines. The name of the class file is in the format dml.???.class.inc where '???' can be MySQL, PostgreSQL, Oracle, SQL Server or whatever. The CONFIG.INC file identifies which database engine is to be used for which database. Although it is usual to have all the databases within a single server instance, it is also possible to have those databases spread across multiple servers on different IP addresses, or even different database engines. It is possible to switch from one database engine to another simply by changing the value in the CONFIG.INC file.

This class is based on the ideas outlined in Using PHP Objects to access your Database Tables (Part 1) and (Part 2). Some of the methods it contains are as follows:

Note that within a single transaction it is possible to access tables in more than one database and through more than one database engine.

View Object

This is item (7) in Figure 5.

It uses as its input the contents of the screen structure script and all the database table objects which were accessed by the controller script.

The screen structure script identifies which XSL stylesheet to use for the HTML output, and a list of field names which need to be displayed in the data area.

The processing steps are as follows:

The HTML output is the text file which is sent back to the client's web browser. This is rendered into a viewable page with the assistance of one or more CSS files which are the recommended way of specifying a standard style in a group of HTML documents.

There are different view objects for creating the output in different formats, such as PDF or CSV.

Screen Structure scripts

This is item (8) in Figure 5.

These are simple scripts which do nothing but identify the view or content for the output screen. Each one identifies the name of an XSL stylesheet and a list of table names, field names and field labels that will be used during the XSL transformation process to produce the HTML output.

The default script for a transaction is automatically created when that transaction is generated from the Data Dictionary.

Sample scripts for each pattern can be found in the /radicore/default/screens/en/ directory with the name <pattern>.screen.inc.

Scripts for each subsystem can be found in the /radicore/<subsystem>/screens/<language>/ directory. The default value for <language> is 'en' (English), but other language codes can be used - refer to Internationalisation and the Radicore Development Infrastructure for details.

Although the parent LIST screen in Figure 7 will require its own Screen Structure file, all the CHILD screens can share the same one as they all use the same structure. The differences in how the fields are displayed for each of the child components is handled by a combination of the $mode parameter within the Transaction Pattern (Controller) script (insert, update, delete, enquire) and individual field attributes within the XML file. These attributes can be specified within the $fieldspec array for that table class, or can be supplied at runtime through custom code.

Here is a sample file:

<?php
// this identifies which XSL stylesheet to use
$structure['xsl_file'] = 'std.detail1.xsl';

// this identifies which XML data is to go into which XSL zone
$structure['tables']['main'] = 'person';

// this specifies the width of each column
$structure['main']['columns'][] = array('width' => 150);
$structure['main']['columns'][] = array('width' => '*');

// the following may also be used 
$structure['main']['columns'][] = array('class' => 'classname');

// this identifies the label and field which is to be displayed in each row
$structure['main']['fields'][] = array('person_id' => 'ID');
$structure['main']['fields'][] = array('first_name' => 'First Name');
$structure['main']['fields'][] = array('last_name' => 'Last Name');
$structure['main']['fields'][] = array('initials' => 'Initials');
$structure['main']['fields'][] = array('nat_ins_no' => 'Nat. Ins. No.');
$structure['main']['fields'][] = array('pers_type_id' => 'Person Type');
$structure['main']['fields'][] = array('star_sign' => 'Star Sign');
$structure['main']['fields'][] = array('email_addr' => 'E-mail');
$structure['main']['fields'][] = array('value1' => 'Value 1');
$structure['main']['fields'][] = array('value2' => 'Value 2');
$structure['main']['fields'][] = array('start_date' => 'Start Date');
$structure['main']['fields'][] = array('end_date' => 'End Date');
$structure['main']['fields'][] = array('selected' => 'Selected');
?>

In this example there is a single data zone called main which is linked with an object called person. Some screens have two or more zones which are linked to different objects. At runtime the fields will be extracted from each object and displayed in the relevant zone. Note that a field must exist both within the object and within the screen structure file in order for it to be displayed.

The name of this file is provided by the Component script in the $screen variable. It is read in by the view object and its contents are added to the XML file to appear something like this:

<root>
  ......
  <structure>
    <main id="person">
      <columns>
        <column width="150"/>
        <column width="*"/>
      </columns>
      <row>
        <cell label="ID"/>
        <cell field="person_id" />
      </row>
      <row>
        <cell label="First Name"/>
        <cell field="first_name"/>
      </row>
      <row>
        <cell label="Last Name"/>
        <cell field="last_name"/>
      </row>
      <row>
        <cell label="Initials"/>
        <cell field="initials"/>
      </row>
      
      ....

      <row>
        <cell label="Start Date"/>
        <cell field="start_date"/>
      </row>
      <row>
        <cell label="End Date"/>
        <cell field="end_date"/>
      </row>
    </main>
  </structure>
</root>

Several different layouts are now available for displaying user data. For more details on how these can be specified please refer to XSL Structure files in The Model-View-Controller (MVC) Design Pattern for PHP.

XML files

This is item (9) in Figure 5.

XML (Extensible Markup Language) is a simple but flexible text format. It is based on an open standard which is maintained by the World Wide Web Consortium. It is used in this infrastructure to provide the XSL transformation process with all the data it needs to produce the HTML output.

The XML file is generated automatically at runtime by the view object. The technique which is used to create this file in PHP 4 is described in Using PHP 4's DOM XML functions to create XML files from SQL data. For PHP 5 refer to Using PHP 5's DOM functions to create XML files from SQL data instead.

Each XML file can contain any of the following data:

XSL Stylesheets

This is item (10) in Figure 5.

Each component requires an XSL stylesheet in order to transform the data in the XML file into HTML output. In an earlier version of this infrastructure I used different stylesheets for each database table which had the table names, field names and field labels all hard-coded, but I have subsequently found a way to use a smaller number of generic stylesheets. Instead of having the field details hard-coded within the stylesheet I am now able to extract that information from within the XML file using information supplied in a Screen Structure script. This is is documented in Reusable XSL Stylesheets and Templates.

Although my whole web application uses fewer than ten generic stylesheets there is still some code which is needed in more than one stylesheet. This code has been extracted and placed in a library of XSL templates which can be incorporated into any stylesheet at runtime by means of an <xsl:include> command. This is, in effect, a library of standard XSL subroutines.

Using the components in Figure 7 as an example I would use a generic LIST stylesheet for the parent component and a generic DETAIL stylesheet for all the child components. Variations in how the individual fields are displayed within the various child components is handled primarily by the $mode variable which is passed as a parameter during the XSL transformation process. This is used as follows:

In addition to the $mode parameter the handling of individual fields can be affected by specific attributes in the XML file. These can either be set into the $fieldspec array or altered at runtime using custom code.

The type of HTML control (textbox, dropdown, radio group, etc) to be used for each field in the HTML output is completely dynamic in nature. This is a 3 stage process:

XSL Transformation process

This is item (11) in Figure 5.

This process will take the contents of an XML document and transform it to another document (in this case an HTML document) using rules contained within an XSL stylesheet. These are all open standards which are supervised by the World Wide Web Consortium.

It is possible to send both the XML and XSL files to the client and have the transformation performed within the client's browser (client-side transformation), but this is unreliable due to the different levels (sometimes non-existent) of XML/XSL support in different browsers. It is much safer to perform the transformation in a single place (the web server) where the software is under the control of the web developer. This is known as a server-side transformation.

The technique which I use to perform XSL transformations in PHP 4 is described in Using PHP 4's Sablotron extension to perform XSL Transformations. For PHP 5 refer to Using PHP 5's XSL extension to perform XSL Transformations instead.

HTML output

This is item (12) in Figure 5.

This is the document which is sent back to the client's browser is response to the request. Its content should conform to the (X)HTML specification which is supervised by the World Wide Web Consortium.

In an effort to make my output viewable on as many web browsers as possible I stick to the following guidelines:

HTML documents can be validated by the W3C Markup Validation Service.

CSS files

This is item (13) in Figure 5.

These are Cascading Style Sheets which hold all the styling information (fonts, colours, sizes, positioning, etc) for all HTML documents produced by the application. The tags within each HTML document refer to a style by a class name, and the specifications for each of these classes is held within a CSS file. In this way it becomes possible to change the style specifications for any tag in all documents simply by changing the specifications within a single CSS file.

The following CSS files are available:

CSS files can be validated using the W3C CSS Validation Service.

AUDIT class

This is item (14) in Figure 5.

This class is responsible for detecting all database changes (INSERTs, UPDATEs and DELETEs) and recording them in a separate 'audit' database so that they can be reviewed using online enquiry screens. This is documented in Creating an Audit Log with an online viewing facility.

The only additional code required in any database table class is the setting of a class variable called $audit_logging. By default this is TRUE (the table will be logged) but it can be set to FALSE to disable logging.

Workflow Engine

This is item (15) in Figure 5.

Sometimes when a particular task is performed, such as 'Take Customer Order', this has to be followed by a series of other tasks in a particular sequence such as 'Charge Customer', 'Pack Order' and 'Ship Order'. Without a Workflow Engine these subsequent tasks must be selected and processed manually, which is where mistakes and inefficiencies can arise.

The purpose of a Workflow System is to manage these tasks in a controlled fashion. This system should have the following components:

The Workflow Engine which I have created as an extension to this development infrastructure is documented in An activity based Workflow Engine for PHP. The engine is activated from within my generic table class therefore no additional programmer coding is required.


Levels of Reusability

The single aspect of any development infrastructure which enables Rapid Application Development (RAD) is the volume of reusable code that it contains. Reusable code in the form of a library of standard modules provides the following advantages:

Although the infrastructure described within this document contains a large number of components it should be pointed out that the majority of them do not require any additional effort on the part of the developer. These are:

The following components are generated automatically at runtime and do not require any programmer effort:

This leaves the following components, which are originally generated by the framework, for the developer to work on:

An idea of the amount of reusability can be shown in figure 10:

Figure 10 - Levels of Reusability

infrastructure-10 (9K)

This shows the following:

I have spent 20+ years working for software houses where the task has been to develop many different applications for many different customers in a swift and cost-effective manner. To be truly reusable an infrastructure should not only work for different components within the same application but for different applications entirely. This offers two advantages:

I have witnessed at first hand the advantages of being able to use such infrastructures as I have used them with two entirely different languages - COBOL and UNIFACE. As I was personally responsible for creating those two infrastructures I had all the relevant experience to create a new one in PHP.


Extending this Infrastructure

Having an infrastructure with which you can build applications is one thing, but in my long experience I have also found it useful to build utility components which may be of benefit to those applications. This type of component is not specific to any particular application, but may be used in conjunction with any number of different applications. Amongst those I have built are:


References


© Tony Marston
2nd August 2003

http://www.tonymarston.net
http://www.radicore.org

Amendment history:

30 April 2012 Added a description for View object.
15 July 2005 Added a reference to a new article entitled Internationalisation and the Radicore Development Infrastructure.
21 June 2005 Amended Screen Structure scripts to show the new layouts caused by the provision of more flexible options.
17 June 2005 Added a reference to a new article entitled A Data Dictionary for PHP Applications.
16 Sep 2004 Added a reference to a new article entitled An activity based Workflow Engine for PHP.
10 Sep 2004 Added section Extending this Infrastructure.
10 Aug 2004 Added better descriptions for the individual components. Moved all the Frequently Asked Questions to a separate document.
03 June 2004 Added a section on Style which explains the advantages of using Cascading Style Sheets.
02 May 2004 Added a reference to The Model-View-Controller (MVC) Design Pattern for PHP.
28 Apr 2004 Added a reference to Reusable XSL Stylesheets and Templates which describes a method which enables me to use a single generic stylesheet for many database tables instead of having a customised stylesheet for each individual database table.
10 Nov 2003 Created a sample application to demonstrate the techniques described in this document. This is described in A Sample PHP Application. The code can be run on my website here, or can be downloaded here and run locally.
08 Sep 2003 Split my abstract database class again so that the code which performs generic validation (declarative checks) is now contained within its own class. Refer to Business layer for details.
31 Aug 2003 Split my abstract database class into two so that the construction and execution of all DML statements is now contained within its own class. Refer to Data Access layer for details.

counter