Guestbook Example

A complete working PHP guestbook application built with Tplix. Covers MVC structure, form validation, PDO database integration, and clean separation of presentation from business logic.

Sample Application

Guestbook

A complete working PHP + Tplix application demonstrating form processing, database integration, and clean presentation separation. Once you understand these concepts you can apply them to any programming pattern.

PHP + Tplix MySQL + PDO Form Validation MVC Pattern

This guide assumes you're already familiar with Apache and MySQL. It is not a guide on how to set those up. You may use this code at your own discretion and risk. Adjust paths and credentials for your environment.

Project File Structure

We'll build on top of the installation guide setup. Here are all the files and directories for the guestbook application:

guestbook app files / directories
# Web server document root (browser-accessible files) /web/www.example.com/docs/ /web/www.example.com/docs/guestbook/ /web/www.example.com/docs/guestbook/index.php # Application files — outside document root (secure) /web/www.example.com/tplix/guestbook/ /web/www.example.com/tplix/guestbook/templates/ # .tpl template files /web/www.example.com/tplix/guestbook/templates_c/ # auto-compiled templates (writable) /web/www.example.com/tplix/guestbook/cache/ # cached output (writable) /web/www.example.com/tplix/guestbook/configs/ # config files /web/www.example.com/tplix/guestbook/libs/ # application logic /web/www.example.com/tplix/guestbook/libs/guestbook_setup.php /web/www.example.com/tplix/guestbook/libs/guestbook.lib.php
index.php Application controller — handles all browser requests and routes to the correct action
guestbook_setup.php Initialisation — loads libraries and configures the Tplix template object
guestbook.lib.php Main application class — database queries, form validation, and template rendering
guestbook.tpl View template — displays the list of guestbook entries
guestbook_form.tpl Form template — the add-entry form with validation error display
1

Application Controller

index.php is the single entry point for the application — the only file the browser accesses directly. It defines paths, loads the setup script, and routes requests to one of three actions: view, add, or submit.

/docs/guestbook/index.php
<?php /** * Guestbook Sample Tplix Application * File: index.php — application controller */ // define application directories define('GUESTBOOK_DIR', '/web/www.example.com/tplix/guestbook/'); define('TPLIX_DIR', '/usr/local/lib/php/Tplix/'); // load the setup script include(GUESTBOOK_DIR . 'libs/guestbook_setup.php'); // create guestbook object $guestbook = new Guestbook; // determine which action was requested (default: view) $_action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view'; switch($_action) { case 'add': // show the blank add-entry form $guestbook->displayForm(); break; case 'submit': // process form submission $guestbook->mungeFormData($_POST); if($guestbook->isValidForm($_POST)) { $guestbook->addEntry($_POST); $guestbook->displayBook($guestbook->getEntries()); } else { $guestbook->displayForm($_POST); // redisplay with errors } break; case 'view': default: // display all entries $guestbook->displayBook($guestbook->getEntries()); break; } ?>
action=view
Display entries
Default — loads and shows all guestbook rows
action=add
Show form
Renders the blank entry form
action=submit
Process form
Validates, saves, or redisplays with errors
2

Setup & Configuration

guestbook_setup.php extends the Tplix class to create a pre-configured template object with all directory paths set automatically whenever it's instantiated.

/libs/guestbook_setup.php
<?php require(GUESTBOOK_DIR . 'libs/guestbook.lib.php'); require(TPLIX_DIR . 'Tplix.class.php'); // extend Tplix with pre-configured paths for this application class Guestbook_Tplix extends Tplix { function __construct() { parent::__construct(); $this->setTemplateDir(GUESTBOOK_DIR . 'templates'); $this->setCompileDir(GUESTBOOK_DIR . 'templates_c'); $this->setConfigDir(GUESTBOOK_DIR . 'configs'); $this->setCacheDir(GUESTBOOK_DIR . 'cache'); } } ?>
3

Database Setup

Run the following SQL script to create the database and table. Adjust the GRANT line to match your own database user and password.

guestbook.sql
CREATE DATABASE GUESTBOOK; USE GUESTBOOK; CREATE TABLE GUESTBOOK ( id int(11) NOT NULL AUTO_INCREMENT, Name varchar(255) NOT NULL DEFAULT '', EntryDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00', Comment text NOT NULL, PRIMARY KEY (id), KEY EntryDate (EntryDate) ) TYPE=MyISAM; -- change user/password to match your environment GRANT ALL ON GUESTBOOK.* TO guestbook@localhost IDENTIFIED BY 'foobar';
4

Application Library

guestbook.lib.php contains the entire application class. Let's walk through each method.

__construct()
Runs once when the Guestbook object is instantiated — sets up DB and template connections
PHP
function __construct() { try { $dsn = "{$this->dbtype}:host={$this->dbhost};dbname={$this->dbname}"; $this->pdo = new PDO($dsn, $this->dbuser, $this->dbpass); } catch (PDOException $e) { print "Error: " . $e->getMessage(); die(); } $this->tpl = new Guestbook_Tplix; }
displayForm($formvars)
Renders the entry form — optionally pre-populating fields and showing a validation error
PHP
function displayForm($formvars = array()) { $this->tpl->assign('post', $formvars); // repopulate fields on error $this->tpl->assign('error', $this->error); // error code for template $this->tpl->display('guestbook_form.tpl'); }
mungeFormData()
Trims whitespace — passed by reference
PHP
function mungeFormData(&$formvars) { $formvars['Name'] = trim($formvars['Name']); $formvars['Comment'] = trim($formvars['Comment']); }
isValidForm()
Validates fields — sets error code on failure
PHP
function isValidForm($formvars) { $this->error = null; if(strlen($formvars['Name']) == 0) { $this->error = 'name_empty'; return false; } if(strlen($formvars['Comment']) == 0) { $this->error = 'comment_empty'; return false; } return true; }
addEntry()
Inserts a new row — PDO handles SQL escaping
PHP
function addEntry($formvars) { try { $rh = $this->pdo->prepare( "INSERT INTO GUESTBOOK VALUES(0,?,NOW(),?)" ); $rh->execute([ $formvars['Name'], $formvars['Comment'] ]); } catch (PDOException $e) { print "Error: " . $e->getMessage(); return false; } return true; }
getEntries()
Fetches all rows ordered newest-first
PHP
function getEntries() { try { foreach($this->pdo->query( "SELECT * FROM GUESTBOOK ORDER BY EntryDate DESC" ) as $row) $rows[] = $row; } catch (PDOException $e) { print "Error: " . $e->getMessage(); return false; } return $rows; }
displayBook($data)
Assigns entries array to template and renders the guestbook view
PHP
function displayBook($data = array()) { $this->tpl->assign('data', $data); $this->tpl->display('guestbook.tpl'); }
5

Templates

guestbook.tpl loops over the entries assigned by displayBook(). It formats the date, HTML-escapes names and comments, and cycles row background colors.

templates/guestbook.tpl
{* Tplix *} <table border="0" width="300"> <tr> <th colspan="2"> Guestbook Entries (<a href="{$SCRIPT_NAME}?action=add">add</a>) </th> </tr> {foreach from=$data item="entry"} <tr bgcolor="{cycle values="#dedede,#eeeeee" advance=false}"> <td>{$entry.Name|escape}</td> <td>{$entry.EntryDate|date_format:"%e %b, %Y %H:%M:%S"}</td> </tr> <tr> <td colspan="2" bgcolor="{cycle values="#dedede,#eeeeee"}"> {$entry.Comment|escape} </td> </tr> {foreachelse} <tr><td colspan="2">No records found.</td></tr> {/foreach} </table>
|escape
HTML-escapes output — prevents XSS and tag conflicts
|date_format
Formats the raw datetime into a human-readable string
{cycle}
Alternates row background colours automatically

guestbook_form.tpl handles the add-entry form. If re-shown after a validation error, it repopulates the fields and displays the correct error message — all without any PHP in the template.

templates/guestbook_form.tpl
{* Tplix *} <form action="{$SCRIPT_NAME}?action=submit" method="post"> <table border="1"> {if $error ne ""} <tr> <td bgcolor="yellow" colspan="2"> {if $error eq "name_empty"} You must supply a name. {elseif $error eq "comment_empty"} You must supply a comment. {/if} </td> </tr> {/if} <tr> <td>Name:</td> <td> <input type="text" name="Name" value="{$post.Name|escape}" size="40"> </td> </tr> <tr> <td valign="top">Comment:</td> <td> <textarea name="Comment" cols="40" rows="10"> {$post.Comment|escape} </textarea> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="Submit"> </td> </tr> </table> </form>
Notice: error messages are driven by error codes (name_empty, comment_empty) — not by PHP strings assigned from the application. The template owns the message wording entirely.

What this application demonstrates

All presentation is in the template
No HTML tags or markup are assigned from PHP. Only data values — names, dates, comments — are assigned. The template decides how to display them.
Error messages are template-owned
PHP assigns an error code. The template translates that code into a human-readable message — keeping wording changes entirely out of PHP.
PHP objects keep things clean
The PDO and Tplix objects are properties on the Guestbook class, available to all methods — no global variables or clunky parameter passing.
Ready to build your own?

Use this as a starting point and apply the same separation pattern to your own projects.

Browse Documentation →