|
 
Linux Journal: Web Applications With PHP And PostgreSQL
Tim Perdue
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();
*/
?>
|