Sample Application: Guestbook

http://www.hbcms.com/main/smarty/index.html

This is an example of a full working PHP application using Smarty. The purpose of this example is to demonstrate how Smarty ties in with an application and how to separate your presentation. The example given is a fairly simple yet complete mini-framework for building Smarty driven applications quickly and easily. Once you understand the concepts of presentation separation, you should be able to apply them to any type of programming pattern. With that said, you may use the following code at your own discretion, and at your own risk.

You can download the source of this Sample Application here.

This is not a guide on how to setup Apache, PEAR or MySQL. Be sure you know these things or have handy references to them. If you are using alternatives, you will need to make appropriate adjustments in the code.

We will be building a guestbook where users can sign it or view it. There is no administration interface. We will be covering a few programming topics that involve Smarty such as form processing and database data retrieval and display.

This example extends the guestbook application setup given in the installation guide for Smarty, so we'll build on top of that. Here are the files we'll start with for our app:

guestbook app files/directories
/web/www.example.com/docs/
/web/www.example.com/docs/guestbook/
/web/www.example.com/docs/guestbook/index.php

/web/www.example.com/smarty/guestbook/
/web/www.example.com/smarty/guestbook/templates/
/web/www.example.com/smarty/guestbook/templates_c/
/web/www.example.com/smarty/guestbook/configs/
/web/www.example.com/smarty/guestbook/cache/

/web/www.example.com/smarty/guestbook/libs/
/web/www.example.com/smarty/guestbook/libs/guestbook_setup.php
/web/www.example.com/smarty/guestbook/libs/guestbook.lib.php
/web/www.example.com/smarty/guestbook/libs/sql.lib.php

Lets go over each one:

/web/www.example.com/docs/

The /docs/ directory is our web server document root.

/web/www.example.com/docs/guestbook/

/guestbook/ is the subdirectory where our application is accessed by the browser.

/web/www.example.com/docs/guestbook/index.php

index.php will be the entry point of our application. The web browser will be accessing this script directly via http://www.example.com/guestbook/index.php.

/web/www.example.com/smarty/guestbook/

This is the directory we will keep all files for our guestbook app that do not need to be under doc root. Whether you choose to keep files under doc root is up to you, but for this example we follow the practice of putting only files directly accessed by the browser there. You could also use Apache .htaccess or other web server means to stop direct access to application files under doc root.

/web/www.example.com/smarty/guestbook/templates/

This where we will put our Smarty template files.

/web/www.example.com/smarty/guestbook/templates_c/

This is where Smarty places its compiled template files. If you installed this correctly, the web server user running PHP has write access here. For most intents and purposes you can just ignore this directory.

/web/www.example.com/smarty/guestbook/configs/

This is where we keep config files for our application. Config files are a place to store information that you want accessible from either the templates or the application. These are not PHP scripts, they are text files parsed by the Smarty config file parser.

/web/www.example.com/smarty/guestbook/cache/

This is where Smarty puts its cache files. This directory is only used if Smarty caching features are enabled. If you installed this correctly, the web server user running PHP has write access here. Much the same as the compile directory, it can be ignored.

/web/www.example.com/smarty/guestbook/libs/

/libs/ is the directory we'll keep our main application files.

/web/www.example.com/smarty/guestbook/libs/guestbook_setup.php

guestbook_setup.php is where we'll keep some basic initialization information for our application.

/web/www.example.com/smarty/guestbook/libs/guestbook.lib.php

guestbook.lib.php is where we'll keep the bulk of our application logic.

/web/www.example.com/smarty/guestbook/libs/sql.lib.php

sql.lib.php is where we keep our database access logic.

 

We'll start with index.php, the entry point of our application. This is the file directly accessed by the web browser.

 

/web/www.example.com/docs/guestbook/index.php
<?php

/**
 * Project: Guestbook Sample Smarty Application
 * Author: Monte Ohrt <monte [AT] ohrt [DOT] com>
 * Date: March 14th, 2005
 * File: index.php
 * Version: 1.0
 */

// define our application directory
define('GUESTBOOK_DIR''/web/www.example.com/smarty/guestbook/');
// define smarty lib directory
define('SMARTY_DIR''/usr/local/lib/php/Smarty/');
// include the setup script
include(GUESTBOOK_DIR 'libs/guestbook_setup.php');

// create guestbook object
$guestbook =& new Guestbook;

// set the current action
$_action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view';

switch(
$_action) {
    case 
'add':
        
// adding a guestbook entry
        
$guestbook->displayForm();
        break;
    case 
'submit':
        
// submitting a guestbook entry
        
$guestbook->mungeFormData($_POST);
        if(
$guestbook->isValidForm($_POST)) {
            
$guestbook->addEntry($_POST);
            
$guestbook->displayBook($guestbook->getEntries());
        } else {
            
$guestbook->displayForm($_POST);
        }
        break;
    case 
'view':
    default:
        
// viewing the guestbook
        
$guestbook->displayBook($guestbook->getEntries());        
        break;   
}

?>

The index.php file acts as the application controller. It handles all incoming browser requests and directs what actions to take. It will define our application directories, include the setup script, and direct an action depending on the action value from the $_REQUEST super-global. We will have three basic actions: add when a user wants to add an entry to the guestbook, submit when a user submits an entry, and view when the user displays the guestbook. The default action is view.

 

/web/www.example.com/smarty/guestbook/libs/guestbook_setup.php
<?php

/**
 * Project: Guestbook Sample Smarty Application
 * Author: Monte Ohrt <monte [AT] ohrt [DOT] com>
 * Date: March 14th, 2005
 * File: guestbook_setup.php
 * Version: 1.0
 */

require(GUESTBOOK_DIR 'libs/sql.lib.php');
require(
GUESTBOOK_DIR 'libs/guestbook.lib.php');
require(
SMARTY_DIR 'Smarty.class.php');
require(
'DB.php'); // PEAR DB

// database configuration
class GuestBook_SQL extends SQL {
    function 
GuestBook_SQL() {
        
// dbtype://user:pass@host/dbname
        
$dsn "mysql://guestbook:foobar@localhost/GUESTBOOK";
        
$this->connect($dsn) || die('could not connect to database');
    }       
}

// smarty configuration
class Guestbook_Smarty extends Smarty 
    function 
Guestbook_Smarty() {
        
$this->template_dir GUESTBOOK_DIR 'templates';
        
$this->compile_dir GUESTBOOK_DIR 'templates_c';
        
$this->config_dir GUESTBOOK_DIR 'configs';
        
$this->cache_dir GUESTBOOK_DIR 'cache';
    }
}
      
?>

guestbook_setup.php is where we do some basic application configuration, such as our database and template configs. We will be using the PEAR::DB library available from PEAR. Be sure DB.php is in your include_path, or supply an absolute path to it. We will be using MySQL as our database, enter the appropriate dsn information for your database setup.

NOTE: If you get a runtime error similar to Call to undefined function: query(), it is likely that your $dsn information is incorrect. Check it twice, test your db connection.

We will be needing a basic database setup. The following is a script that you can dump directly into MySQL with mysql < guestbook.sql. Be sure you change the GRANT line with your database/user information.

 

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;

GRANT ALL ON GUESTBOOK.* to guestbook@localhost identified by 'foobar';

 

/web/www.example.com/smarty/guestbook/libs/sql.lib.php
<?php

/**
 * Project: Guestbook Sample Smarty Application
 * Author: Monte Ohrt <monte [AT] ohrt [DOT] com>
 * Date: March 14th, 2005
 * File: sql.lib.php
 * Version: 1.0
 */

// define the query types
define('SQL_NONE'1);
define('SQL_ALL'2);
define('SQL_INIT'3);

// define the query formats
define('SQL_ASSOC'1);
define('SQL_INDEX'2);

class 
SQL {
    
    var 
$db null;
    var 
$result null;
    var 
$error null;
    var 
$record null;
    
    
/**
     * class constructor
     */
    
function SQL() { }
    
    
/**
     * connect to the database
     *
     * @param string $dsn the data source name
     */
    
function connect($dsn) {
        
$this->db DB::connect($dsn);

        if(
DB::isError($this->db)) {
            
$this->error $this->db->getMessage();
            return 
false;
        }        
        return 
true;
    }
    
    
/**
     * disconnect from the database
     */
    
function disconnect() {
        
$this->db->disconnect();   
    }
    
    
/**
     * query the database
     *
     * @param string $query the SQL query
     * @param string $type the type of query
     * @param string $format the query format
     */
    
function query($query$type SQL_NONE$format SQL_INDEX) {

        
$this->record = array();
        
$_data = array();
        
        
// determine fetch mode (index or associative)
        
$_fetchmode = ($format == SQL_ASSOC) ? DB_FETCHMODE_ASSOC null;
        
        
$this->result $this->db->query($query);
        if (
DB::isError($this->result)) {
            
$this->error $this->result->getMessage();
            return 
false;
        }
        switch (
$type) {
            case 
SQL_ALL:
                
// get all the records
                
while($_row $this->result->fetchRow($_fetchmode)) {
                    
$_data[] = $_row;   
                }
                
$this->result->free();            
                
$this->record $_data;
                break;
            case 
SQL_INIT:
                
// get the first record
                
$this->record $this->result->fetchRow($_fetchmode);
                break;
            case 
SQL_NONE:
            default:
                
// records will be looped over with next()
                
break;   
        }
        return 
true;
    }
    
    
/**
     * connect to the database
     *
     * @param string $format the query format
     */
    
function next($format SQL_INDEX) {
        
// fetch mode (index or associative)
        
$_fetchmode = ($format == SQL_ASSOC) ? DB_FETCHMODE_ASSOC null;
        if (
$this->record $this->result->fetchRow($_fetchmode)) {
            return 
true;
        } else {
            
$this->result->free();
            return 
false;
        }
            
    }
    
}

?>

sql.lib.php is our database wrapper class around PEAR::DB. It will help keep the database access syntax in our application to a minimum. You can just copy and paste the above code, don't worry too much about understanding it unless you feel inclined. Here is a crash course on the usage:

 

$guestbook->sql->query("select * from GUESTBOOK", SQL_ALL);
print_r($guestbook->sql->record);   
OUTPUT:
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => Monte
            [2] => 2005-03-12 17:23:32
            [3] => test entry 1
        )

    [1] => Array
        (
            [0] => 2
            [1] => Monte
            [2] => 2005-03-12 17:23:33
            [3] => test entry 2
        )

    [2] => Array
        (
            [0] => 3
            [1] => Monte
            [2] => 2005-03-12 17:23:35
            [3] => test entry 3
        )

)

All of the guestbook entries are shown. SQL_ALL will get all of the query records.

 

$guestbook->sql->query("select * from GUESTBOOK");
while($guestbook->sql->next()) {
    print_r($guestbook->sql->record);
}   
OUTPUT:
Array
(
    [0] => 1
    [1] => Monte
    [2] => 2005-03-12 17:23:32
    [3] => test entry 1
)

Array
(
    [0] => 2
    [1] => Monte
    [2] => 2005-03-12 17:23:33
    [3] => test entry 2
)

Array
(
    [0] => 3
    [1] => Monte
    [2] => 2005-03-12 17:23:35
    [3] => test entry 3
)

This loops over the records one by one. If no second parameter is supplied to query(), then the resulting records are looped over with next().

 

$guestbook->sql->query("select * from GUESTBOOK", SQL_INIT);
print_r($guestbook->sql->record);
OUTPUT:
Array
(
    [0] => 1
    [1] => Monte
    [2] => 2005-03-12 17:23:32
    [3] => test entry 1
)

This outputs only one record (the first one). SQL_INIT will get one record only.

 

$guestbook->sql->query("select * from GUESTBOOK", SQL_INIT, SQL_ASSOC);
print_r($guestbook->sql->record);
OUTPUT:
Array
(
    [id] => 1
    [Name] => Monte
    [EntryDate] => 2005-03-12 17:23:32
    [Comment] => test entry 1
)

Passing a third parameter of SQL_ASSOC to query() will return the results as an associative array: fieldname => value.

 

$guestbook->sql->query("select * from GUESTBOOK");
while($guestbook->sql->next(SQL_ASSOC)) {
    print_r($guestbook->sql->record);
}   
OUTPUT:
Array
(
    [id] => 1
    [Name] => Monte
    [EntryDate] => 2005-03-12 17:23:32
    [Comment] => test entry 1
)

Array
(
    [id] => 2
    [Name] => Monte
    [EntryDate] => 2005-03-12 17:23:33
    [Comment] => test entry 2
)

Array
(
    [id] => 3
    [Name] => Monte
    [EntryDate] => 2005-03-12 17:23:35
    [Comment] => test entry 3
)

Passing SQL_ASSOC as a parameter to next() will also return results as an associative array.

 

 

/web/www.example.com/smarty/guestbook/libs/guestbook.lib.php
<?php

/**
 * Project: Guestbook Sample Smarty Application
 * Author: Monte Ohrt <monte [AT] ohrt [DOT] com>
 * Date: March 14th, 2005
 * File: guestbook.lib.php
 * Version: 1.0
 */

/**
 * guestbook application library
 *
 */
class Guestbook {

    
// database object
    
var $sql null;
    
// smarty template object
    
var $tpl null;
    
// error messages
    
var $error null;
    
    
/**
     * class constructor
     */
    
function Guestbook() {

        
// instantiate the sql object
        
$this->sql =& new GuestBook_SQL;
        
// instantiate the template object
        
$this->tpl =& new Guestbook_Smarty;

    }
    
    
/**
     * display the guestbook entry form
     *
     * @param array $formvars the form variables
     */
    
function displayForm($formvars = array()) {

        
// assign the form vars
        
$this->tpl->assign('post',$formvars);
        
// assign error message
        
$this->tpl->assign('error'$this->error);
        
$this->tpl->display('guestbook_form.tpl');

    }
    
    
/**
     * fix up form data if necessary
     *
     * @param array $formvars the form variables
     */
    
function mungeFormData(&$formvars) {

        
// trim off excess whitespace
        
$formvars['Name'] = trim($formvars['Name']);
        
$formvars['Comment'] = trim($formvars['Comment']);

    }

    
/**
     * test if form information is valid
     *
     * @param array $formvars the form variables
     */
    
function isValidForm($formvars) {

        
// reset error message
        
$this->error null;
        
        
// test if "Name" is empty
        
if(strlen($formvars['Name']) == 0) {
            
$this->error 'name_empty';
            return 
false
        }

        
// test if "Comment" is empty
        
if(strlen($formvars['Comment']) == 0) {
            
$this->error 'comment_empty';
            return 
false
        }
        
        
// form passed validation
        
return true;
    }
    
    
/**
     * add a new guestbook entry
     *
     * @param array $formvars the form variables
     */
    
function addEntry($formvars) {

        
$_query sprintf(
            
"insert into GUESTBOOK values(0,'%s',NOW(),'%s')",
            
mysql_escape_string($formvars['Name']),
            
mysql_escape_string($formvars['Comment'])
        );
        
        return 
$this->sql->query($_query);
        
    }
    
    
/**
     * get the guestbook entries
     */
    
function getEntries() {

        
$this->sql->query(
            
"select * from GUESTBOOK order by EntryDate DESC",
            
SQL_ALL,
            
SQL_ASSOC
        
);

        return 
$this->sql->record;   
    }
    
    
/**
     * display the guestbook
     *
     * @param array $data the guestbook data
     */
    
function displayBook($data = array()) {

        
$this->tpl->assign('data'$data);
        
$this->tpl->display('guestbook.tpl');        

    }
}

?>

guestbook.lib.php is our application class. It contains the main logic for our entire application. Lets look at each class method.

 

class method Guestbook()
/**
 * class constructor
 */
function Guestbook() {

    // instantiate the sql object
    $this->sql =& new GuestBook_SQL;
    // instantiate the template object
    $this->tpl =& new Guestbook_Smarty;

}

This is the class constructor. It is executed each time we instantiate the guestbook object. It instantiates the SQL and Smarty objects as properties. We can then access them from within our object methods.

 

class method displayForm()
/**
 * display the guestbook entry form
 *
 * @param array $formvars the form variables
 */
function displayForm($formvars = array()) {

    // assign the form vars
    $this->tpl->assign('post',$formvars);
    // assign error message
    $this->tpl->assign('error', $this->error);
    $this->tpl->display('guestbook_form.tpl');

}

The displayForm() method is used for displaying the guestbook entry form. It assigns the form variables and the form validation error message to the template, then displays the form.

 

class method mungeFormData()
/**
 * fix up form data if necessary
 *
 * @param array $formvars the form variables
 */
function mungeFormData(&$formvars) {

    // trim off excess whitespace
    $formvars['Name'] = trim($formvars['Name']);
    $formvars['Comment'] = trim($formvars['Comment']);

}

The mungeFormData() method trims off whitespace from the form input. This is called prior to form validation. Notice the form data is passed into the method by reference so the changes will affect the original array.

 

class method isValidForm()
/**
 * test if form information is valid
 *
 * @param array $formvars the form variables
 */
function isValidForm($formvars) {

    // reset error message
    $this->error = null;

    // test if "Name" is empty
    if(strlen($formvars['Name']) == 0) {
        $this->error = 'name_empty';
        return false;
    }

    // test if "Comment" is empty
    if(strlen($formvars['Comment']) == 0) {
        $this->error = 'comment_empty';
        return false;
    }

    // form passed validation
    return true;
}

The method isValidForm() validates the form input. This is a simple test to see if the Name or Comment was empty. If so, the appropriate error code is assigned to the error property. (These error codes are used by the template later on.)

 

class method addEntry()
/**
 * add a new guestbook entry
 *
 * @param array $formvars the form variables
 */
function addEntry($formvars) {

    $_query = sprintf(
        "insert into GUESTBOOK values(0,'%s',NOW(),'%s')",
        mysql_escape_string($formvars['Name']),
        mysql_escape_string($formvars['Comment'])
    );

    return $this->sql->query($_query);

}

The addEntry method enters a new guestbook entry into the database. Notice the values are escaped to avoid SQL syntax errors or injection attacks.

 

class method getEntries()
/**
 * get the guestbook entries
 */
function getEntries() {

    $this->sql->query(
        "select * from GUESTBOOK order by EntryDate",
        SQL_ALL,
        SQL_ASSOC
    );

    return $this->sql->record;   
}

The method getEntries() gets all the guestbook entries from the database in field => value format (SQL_ASSOC).

 

class method displayBook()
/**
 * display the guestbook
 *
 * @param array $data the guestbook data
 */
function displayBook($data = array()) {

    $this->tpl->assign('data', $data);
    $this->tpl->display('guestbook.tpl');        

}

The method displayBook() displays the guestbook entries. The $data array is expected to be an array of the guestbook entries. This is assigned to the template and then the template is displayed.

 

We have two templates for our guestbook, one for viewing and one for adding a new entry.

 

/web/www.example.com/smarty/guestbook/templates/guestbook.tpl
{* Smarty *}

<table border="0" width="300">
    <tr>
        <th colspan="2" bgcolor="#d1d1d1">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 align="right">{$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</td>
        </tr>
    {/foreach}
</table>

guestbook.tpl is the template for viewing the guestbook. It loops over the guestbook data (which was assigned from displayBook()) in a foreach loop and displays the Name, Date and Comment from each entry. The date is formatted with the date_format modifier. The Name and Comment are HTML-escaped using the escape modifier to avoid any HTML tag clashes or scripting attacks. The {cycle} function is used to cycle through background colors every two table rows.

 

/web/www.example.com/smarty/guestbook/templates/guestbook_form.tpl
{* Smarty *}

<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>

guestbook_form.tpl is the template for adding an entry to the guestbook. If the form is being redisplayed due to a validation error, the form values are repopulated and the appropriate error message is displayed. The form values are HTML-escaped so there are no HTML tag or quote character clashes (very important!)

With this sample application we have accomplished several key aspects of a Smarty driven application.

* All presentation elements are contained in the template. We don't assign HTML tags or other presentation elements from outside the template. The only thing we assign is the page content, in this case the guestbook entries.

* Error messages are also maintained from the template. We don't assign error messages themselves, but error codes which are used to determine which error message to display. An alternative way to maintain error messages are from within Smarty config files, where you can have error_code = Error Message in the config file, then displayed with {$smarty.config.$error_code}

* PHP objects are used extensively to show their usefulness for easily passing information around (such as sql/template objects and error codes) avoiding procedural functions and clunky parameter passing.

Hopefully this gives you an idea how to setup your applications to work with Smarty in a way that cleanly separates the application from its presentation.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值