PHPBuilder - Serious Web Applications With PHP & PostgreSQL



RSS Twitter
Articles Databases

Serious Web Applications With PHP & PostgreSQL

by: Tim Perdue
|
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

Comment and Contribute

Your comment has been submitted and is pending approval.

Author:
Tim Perdue

Comment:



Comment:

(Maximum characters: 1200). You have characters left.