#native_company# #native_desc#
#native_cta#

Serious Web Applications With PHP & PostgreSQL

By Tim Perdue
on December 3, 2000

Here is the code from my Linux Journal article on how to build a
business application using PostgreSQL’s transactions and PHP4.

Common.php

<?php

//connect to postgres database

$conn=pg_pconnect("user=tperdue dbname=linuxjournal");

//see if our connection was successful

if (!$conn) {

    
//connection failed - exit the page with an error

    //you could also try to proceed without the 

    //database - it's up to you

    
echo pg_errormessage($conn);

    exit;

}

//now let's set up a common site header

function site_header ($title) {

    return 
'<HTML>

    <HEAD>

    <TITLE>'
.$title.'</TITLE>

    </HEAD>

    <BODY>'
;

}

//common HTML to be output at the end of the page

function site_footer () {

    return '</BODY></HTML>';

}

//a simple wrapper to reduce the code needed

//for each postgres query

function query($sql) {

    global 
$conn;

    return 
pg_exec($conn,$sql);

}

//have PHP4 set up/restore your session state automagically

//on every page

session_start();

/*

create sequence seq_customer_id increment 26 start 1;

create table customers (

customer_number int not null default 0 primary key,

name text,

address text,

credit_card text,

total_order MONEY DEFAULT '$0.00'

);

create table cart_items (

cart_item serial,

customer_id int,

part_number int,

quantity int

);

create index idx_cart_customer on cart_items(customer_number);

create table item_inventory (

part_number serial,

name text,

price float,

inventory int

);

*/

function cart_new() {

    
//make the database connection handle available

    
global $conn,$customer_id,$feedback;

    //start a transaction

    
query("BEGIN WORK");

    //query postgres for the next value in our sequence

    
$res=query("SELECT nextval('seq_customer_id')");

    //check for errors

    
if (!$res || pg_numrows($res)<1) {

        
$feedback .= pg_errormessage($conn);

        
$feedback .= ' Error - Database didn't return next value ';

        
query("ROLLBACK");

        return 
false;

    } else {

        
//set that value in a local var

        
$customer_id=pg_result($res,0,0);

        //register the id with PHP4

        
session_register('customer_id');

        //insert the new customer row

        
$res=query("INSERT INTO customers (customer_id) VALUES ('$customer_id')");

        //check for errors

        
if (!$res || pg_cmdtuples($res)<1) {

            
$feedback .= pg_errormessage($conn);

            
$feedback .= ' Error - couldn't insert new customer row ';

            
query("ROLLBACK");

            return 
false;

        } else {

            
//commit this transaction

            
query("COMMIT");

            return 
true;

        }

    }

}

function cart_add_item($item_id,$quantity=1) {

    global 
$customer_id,$feedback,$conn

    //no need to start a transaction, as only one query

    //will be updating the database

    //query postgres for the next value in our sequence

    $res=query("SELECT * FROM item_inventory WHERE part_number='$item_id'");

    //check for errors

    
if (!$res || pg_numrows($res)<1) {

        
$feedback .= pg_errormessage($conn);

        
$feedback .= ' Error - item not found ';

        return 
false;

    } else {

        
//item was legit - see if already in cart. If so, increment quantity

        //start a transaction so we can lock the rows if they are found

        
query("BEGIN WORK");

        $res=query("SELECT * FROM cart_items ".

            
"WHERE part_number='$item_id' AND customer_id='$customer_id' FOR UPDATE");

    

        
//check for errors

        
if (!$res || pg_numrows($res)<1) {

            
//insert it into the cart

            $res=query("INSERT INTO cart_items ".

                
"(customer_id,part_number,quantity) ".

                
"VALUES ($customer_id,$item_id,$quantity)");

            //check for errors on insert

            
if (!$res || pg_cmdtuples($res) < 1) {

                
$feedback .= pg_errormessage($conn);

                
$feedback .= ' Error - couldn't insert into cart ';

                
//nothing was changed but it's good form to cancel the transaction

                
query("ROLLBACK");

                return 
false;

            } else {

                
query("COMMIT");

                return 
true;

            }

        } else {

            
//item already in cart - increment quantity

            
$res=query("UPDATE cart_items SET quantity = quantity + $quantity ".

                
"WHERE part_number='$item_id' AND customer_id='$customer_id'");

            if (!
$res || pg_cmdtuples($res) < 1) {

                
$feedback .= pg_errormessage($conn);

                
$feedback .= ' Error - couldn't increment quantity in cart ';

                
//again nothing was changed

                                
query("ROLLBACK");

                return 
false;

            } else {

                
//commit the updated quantity to the database

                
query("COMMIT");

                return 
true;

            }

        }

    }

}

function cart_checkout($credit_card,$address,$name) {

    global 
$conn,$customer_id,$feedback;

    //start a transaction

    
query("BEGIN WORK");

    /*

        lock the appropriate rows in the item_inventory table,

        based on what is in the visitor's cart

        We'll do this with a simple subselect

        and Postgres' SELECT ... FOR UPDATE syntax

    */

    $sql="SELECT * FROM item_inventory ".

        
"WHERE part_number ".

        
"IN (SELECT part_number FROM cart_items ".

        
"WHERE customer_id='$customer_id') ".

        
"FOR UPDATE";

    
$res=query($sql);

    //check for errors

    
if (!$res || pg_numrows($res)<1) {

        
//no items matched or db failed

        
$feedback .= pg_errormessage($conn);

        
$feedback .= ' Error - no items locked ';

        //terminate the transaction

        
query("END WORK");

        return 
false;

    } else {

        
/*

            Inventory rows are now locked

            Get the items and quantity from the cart

        */

        $sql="SELECT part_number,quantity ".

            
"FROM cart_items ".

            
"WHERE customer_id='$customer_id' ".

            
"ORDER BY part_number DESC";

        
$res2=query($sql);

        //check for errors

        
if (!$res2 || pg_numrows($res2)<1) {

            
//no items selected from cart

            
$feedback .= pg_errormessage($conn);

            
$feedback .= ' Error - no items in cart ';

            //terminate the transaction

            
query("END WORK");

            return 
false;

        } else {

            
$rows=pg_numrows($res2);

            
/*

                Inventory is locked and we have the cart contents

                Iterate and update the inventory balances

            */

            for ($i=0$i $rows$i++) {

                
//fetch this row from our cart

                
$quantity=pg_result($res2,$i,'quantity');

                
$item_id=pg_result($res2,$i,'part_number');

                $res3=query("UPDATE item_inventory ".

                    
"SET inventory = inventory-$quantity ".

                    
"WHERE part_number='$item_id'");

                //see if query failed or no rows were affected

                
if (!$res3 || pg_cmdtuples($res3) < 1) {

                    
//couldn't update this row

                    
$feedback .= pg_errormessage($conn);

                    
$feedback .= ' Error - updating inventory failed ';

                    //rollback the transaction

                    
query("ROLLBACK");

                    return 
false;

                }

            }

            /*

                Inventory now fully updated

                Finally - let's get the total amount

                of this order and update the customer record

            */

            $res=query("SELECT sum(cart_items.quantity*item_inventory.price) ".

                
"FROM cart_items,item_inventory ".

                
"WHERE cart_items.customer_id='$customer_id' ".

                
"AND cart_items.part_number=item_inventory.part_number");

            //check for errors

            
if (!$res || pg_numrows($res) < 1) {

                
//couldn't get order total

                
$feedback .= pg_errormessage($conn);

                
$feedback .= ' Error - couldn't get order total ';

            

                
//rollback the transaction

                
query("ROLLBACK");

                return 
false;

            } else {

                
/*

                    We've got the order total, now 

                    we just update the customers table

                */

                //set the sum as a local variable

                $total=pg_result($res,0,0);

                
$res=query("UPDATE customers ".

                    
"SET address='$address',name='$name',".

                    
"total_order='$total',credit_card='$credit_card' ".

                    
"WHERE customer_id='$customer_id'");

                //the usual error check

                
if (!$res || pg_cmdtuples($res) < 1) {

                    
//update failed or did not affect any rows

                    
$feedback .= pg_errormessage($conn);

                    
$feedback .= ' Error - updating customer information ';

                    //rollback the transaction

                    
query("ROLLBACK");

                    return 
false;

                } else {

                    
/*

                        We made it! 

                        Now commit the changes to the database

                    */

                    //commit all changes

                    
query("COMMIT");

                    //erase the PHP4 session

                    
$customer_id=0;

                    
session_destroy();

                    return 
true;

                }

            }

        }

    }

}

/*

//

//    Test code

//

$res=cart_new();

echo $feedback;

echo site_header('Linux Journal');

echo '<P>'.$customer_id;

$res=cart_add_item(1);

echo $feedback;

$res=cart_add_item(2);

echo $feedback;

$res=cart_checkout('1234 5678 9102 3456','1257 Lakeside Drive, #4228 SunnyVale, CA','Tim Perdue');

echo $feedback;

echo site_footer();

*/

?>



Download: linuxjournal200009.zip