FETCH_PROBLEM ( ) This function, which essentially does the same thing as the previous three, takes a slightly different form. This is because the fetch_record()

FETCH_PROBLEM ( ) This function, which essentially does the same thing as the previous three, takes a slightly different form. This is because the fetch_record()

function can only assemble simple queries: one table is all it can manage. However, since the set_result_vars() function is the one that actually assigns the results of the query to globals, you can pass the results of a query directly to it.

function fetch_problem ($problem_id = “”, $problem_code = “”) {

$query = “select p.* , s.status , u.source from problems p left join status s on p.status_id = s.status_id left join sources u on p.source_id = u.source_id

“; if (!empty($problem_id)) {

$query .= “ where p.problem_id = $problem_id “;

} else {

$query .= “ where p.problem_code = ‘$problem_code’

342 Part IV: Not So Simple Applications

“; } $result = safe_query($query); if (!$result) { die(“no such problem: <pre>$query</pre>”); } set_result_variables($result); return $result;

} Here you are gathering all the information associated with a single problem_id.

For that, you need to join problems on the sources status tables. FIND_CUSTOM ER( ) Remember that you would like to enable users to report their

problems over the Web. In this application, we’ve decided that while there is a numeric primary key for each user, the application should be able to identify the user by either a phone number or an e-mail. So when a user enters information, you will need to check if someone with an identical e-mail or phone number has come along.

function find_customer($email=”” ,$day_area=””,$day_prefix=””,$day_suffix=”” ,$eve_area=””,$eve_prefix=””,$eve_suffix=””

$where = “”; $sep = “”; if ($day_prefix != “”) {

// there must be a prefix for this to be a valid phone number $where .= “

(day_area = ‘$day_area’ and day_prefix = ‘$day_prefix’ and day_suffix = ‘$day_suffix’

“; // separate each part of the qualification with OR -

// any part constitutes a valid match. $sep = “ or “;

} if ($eve_prefix != “”) {

// there must be a prefix for this to be a valid phone //number

Chapter 13: Problem Tracking System 343

$where .= “ $sep (eve_area = ‘$eve_area’

and eve_prefix = ‘$eve_prefix’ and eve_suffix = ‘$eve_suffix’

) “; $sep = “ or “;

} if ($email != “”) {

$where .= “ $sep (email = ‘$email’)

“; } if ($where == “”) {

// nothing to look for return FALSE;

} // run a query with the constructed qualification

// and return the result $query = “select * from customers

where $where order by customer_id

“; $result = safe_query($query); return $result;

} With this function you will know if the user has an existing record that can be

used or that might need to be updated. Notice the grouping of the portions of the where clause. It is looking for any one of three circumstances, each of which must meet a few criteria. If the e-mail, day- time phone and evening phone fields are filled in, this function will create a query that looks like this:

select * from customers where (day_area = ‘415’

and day_prefix = ‘555’ and day_suffix = ‘0410’ )

or (eve_area = ‘212’ and eve_prefix = ‘555’

344 Part IV: Not So Simple Applications

and eve_suffix = ‘9999’ ) or (email = ‘jay@trans-city.com’) order by customer_id

Tip If you were interested, you could set a cookie to make identifying the user a bit easier.

PRESENT_DUPS( ) You need to plan for a couple of eventualities: if there are iden- tical e-mail addresses or phone numbers, but some other personal information has changed, you need to let the user either update the database or discard the data. This function spots the redundancy and alerts the user.

function present_dups ($result) {

// we have to start the call entry form inside the function - // use a global variable to indicate that this was done. global $in_form; $in_form = 1;

// start the form print start_form(“create_call.php”);

print paragraph(“<b>”

.”We may have found you in our database.” .” Please let us know what you would like to do:” .”</b>”

); print start_table(); // for each customer record in the result set

while ($row = mysql_fetch_array($result,MYSQL_ASSOC)) {

// print out the ID value for the record in a radio field, // allowing the user to choose only one if more than // one is displayed. print table_row(

Chapter 13: Problem Tracking System 345

radio_field( “customer_id” , $row[“customer_id”] , “<b>Use this row</b>”

) , “<b>Record #”.$row[“customer_id”].”</b>”

); // print out the name & address information from this record

print table_row(“”,$row[“firstname”].” “.$row[“lastname”]); if (!empty($row[“address”])) {

print table_row(“”,$row[“address”]); } if (!empty($row[“address2”])) {

print table_row(“”,$row[“address2”]); } if ( !empty($row[“city”]) || !empty($row[“state”])

|| !empty($row[“zip”]) ) {

print table_row(“” , $row[“city”].”, “.$row[“state”].” “ .$row[“zip”] ); } if (!empty($row[“day_prefix”])) {

$daycell = “Day: “ .$row[“day_area”] .” “ .$row[“day_prefix”] .”-” .$row[“day_suffix”]

; if (!empty($row[“day_ext”])) {

$daycell .= “ “.$row[“day_ext”]; } if (!empty($row[“day_start”])) {

346 Part IV: Not So Simple Applications

$daycell .= “ from “.$row[“day_start”];

} if (!empty($row[“day_end”])) {

$daycell .= “ until “.$row[“day_end”];

} print table_row(“”,$daycell);

} if (!empty($row[“eve_prefix”])) {

$evecell = “Eve: “ .$row[“eve_area”] .” “ .$row[“eve_prefix”] .”-” .$row[“eve_suffix”]

; if (!empty($row[“eve_ext”])) {

$evecell .= “ “.$row[“eve_ext”];

} if (!empty($row[“eve_start”])) {

$evecell .= “ from “.$row[“eve_start”];

} if (!empty($row[“eve_end”])) {

$evecell .= “ until “.$row[“eve_end”];

} print table_row(“”,$evecell);

} if (!empty($row[“email”])) {

print table_row(“”,$row[“email”]); }

// print out a checkbox field allowing the user to // indicate that this record should be overwritten // with the information from the form. print table_row(“”

, checkbox_field( “override”

Chapter 13: Problem Tracking System 347

, $row[“customer_id”] , “<b>Override this entry with the new”

.” information in the form below.</b>”

// print out a checkbox field allowing the user to // indicate that this record should be merged // with the information from the form and the result // written back to the database. print table_row(“”

, checkbox_field( “merge” , $row[“customer_id”] , “<b>Merge this entry with the new”

.” information in the form below.</b>”

) ); } print end_table();

// print out a final radio field indicating that, rather than // using any of the records found in the database, a new record // should be created. print paragraph(radio_field(

“add_as_new” , “yes” , “Create a new record with the information in the form

below.” ));

} All the duplicate rows are printed, and the user can choose what to do with the

data. You can see this in action if you go to the index.php page of this application and attempt to enter two different tickets with, say, the same phone number. Figure 13-6 gives an example.

348 Part IV: Not So Simple Applications

Figure 1 3 - 6 : Form for updating customer information

HISTORY_ENTRY( ) When a staff member enters an update on a problem, the step is stored in the history table. If the entry is “public” the user will be e-mailed with the update; if not, there will be no e-mail.

Notice the interesting query. Here the insert contains a select statement. The only thing this select is actually getting is the source_id related to the variable $source . All the rest of the insert information comes from variables.

function history_entry ($problem_id=””,$entry_type_id=””,$entered_by=””

,$source=””,$notes=”” ) {

if (empty($problem_id)) { return FALSE; } if (empty($entered_by)) { $entered_by = “customer”; } // create a record in the history table, getting the ID value

// of the source from the sources table. $query = “insert into history

(problem_id,entry_type_id,entered_by,source_id,notes) select ‘$problem’_id,$entry_type_id,’$entered_by’,

Chapter 13: Problem Tracking System 349

source_id, ‘$notes’ from sources where source = ‘$source’ “; $result = safe_query($query); if ($result) {

// get the ID value of the new history record // (automatically assigned by MySQL). $entry_id = mysql_insert_id();

// get the email address of // the customer who opened this call // if this was a public history // entry, and if the email address // is not empty $cresult = safe_query(“select c.email

from problems p, customers c, history h, entry_types et where h.entry_id = $entry_id and et.entry_type_id = h.entry_type_id and et.entry_type = ‘public’ and h.problem_id = p.problem_id and p.customer_id = c.customer_id and c.email != ‘’ and c.email is not null

“); if ($cresult && mysql_num_rows($cresult)) {

// we have a valid email address - use it to // notify the customer that the call record // has been updated. list($email) = mysql_fetch_array($cresult); notify_customer(‘$problem_id’,$email,$notes);

// return the result of the creation of the new history record return $result;

} If the update is public, the notify_customer() function is run. NOTIFY_CUSTOM ER( ) This function constructs an e-mail and sends it. function notify_customer ($problem_id=””, $email=””, $notes=””,

$problem_code=””) {

350 Part IV: Not So Simple Applications

// the Apache global variable $SERVER_NAME is the name // of the server we’re running on, minus any port number. global $SERVER_NAME;

// remove any HTML tags and backslashes from $notes. $notes = stripslashes(cleanup_text($notes));

if (empty($problem_code)) {

$result = safe_query(“select problem_code from

problems where problem_id = $problem_id

“); $problem_code = mysql_result($result,0); if (empty($problem_code)) {

$problem_code = create_problem_code(); safe_query(“update problems

set problem_code = ‘$problem_code’ where problem_id = $problem_id

“); } // build an absolute URL calling the problem_status.php page // to check on this problem $problem_url =

regular_url(“problem_status.php?problem_code=$problem_code”); // set the body of the email

$msgtext = <<<EOQ Problem Update: $notes You can check the current status of this problem at $problem_url

Thanks for your patience. EOQ;

// set the headers of the email $headers = “From: webmaster@”.$SERVER_NAME.”\n”

.”Reply-To: webmaster@”.$SERVER_NAME.”\n” .”X-Mailer: PHP/”.phpversion()

Chapter 13: Problem Tracking System 351

; // send the email

return mail($email, “Problem Update”, $msgtext, $headers); }

N OTE

PHP w ill have to be able to find sendm ail or anot her SMTP-com pliant m ail server in order for t his to work. Check your php.ini file is you’re having problem s.

STATUS_CHANGE( ) The status of a problem is going to be something like “open,” “closed,” or “pending.” If it changes you are going to want to mark the exact change and record something like “status changed to closed by John.” The change should be recorded in the history table.

function status_change($problem_id=”” ,$new_status_id=”” ,$old_status_id=”” , $entered_by=”customer”

if (empty($problem_id) || empty($new_status_id) || $old_status_id == $new_status_id ) {

return; }

if (empty($entered_by)) { $entered_by = “customer”; } // get the ID of the entry_type ‘public’, and construct

// a string containing the new status value and either // the real name of the staff member who made the change, // or the value of $entered_by if no matching staff // member is found. for example, if the staff member Joe Blow // closes a call, the notes field will be set to // “Status set to Closed by Joe Blow”. if a customer // re-opens a call, notes will be set to // “Status set to Re-opened by customer”.

// all of this depends on the value in $new_status_id // being a valid ID of a record in the status table. $query = “select et.entry_type_id

352 Part IV: Not So Simple Applications

, concat(‘Status set to ‘ , ns.status , ‘ by ‘ , ifnull(t.staff_name,’$entered_by’)

) as notes from entry_types et, status ns left join staff t on t.username = ‘$entered_by’ where et.entry_type = ‘public’ and ns.status_id = $new_status_id “;

$result = safe_query($query); if ($result) {

// $new_status_id is a valid status ID - use the // history_entry() to make an entry in the history table // recording the status change, and send email notifiying // the user. list($entry_type_id, $notes) = mysql_fetch_array($result); history_entry($problem_id, $entry_type_id, $entered_by

, ‘program’, $notes

DISPLAY_CALL_LIST( ) This is another function of convenience. It prints the results of a query along with a header row.

function display_call_list($query=””,$subtitle=”Call List”

, $script=”edit_problem.php” ) {

if ($query == “”) { return; } $result = safe_query($query); if (!$result) { return; }

$calls = 0; while ($row = mysql_fetch_array($result)) {

if ($calls == 0) {

// we want to print out the table header only once, // and only if there is at least one row - // do it when processing the first row // of the result set.

Chapter 13: Problem Tracking System 353

print subtitle($subtitle); print start_table(); print table_row(

“<b>Problem #</b>” , “<b>Date</b>” , “<b>Customer</b>” , “<b>Problem</b>” , “<b>Source</b>” , “<b>Status</b>”

); } $calls++;

// print out information about the call, including // a link to the given script for updating its // status and history. print table_row($row[“problem_id”]

, $row[“entry_dt”] , anchor_tag($script.”?problem_id=”

.$row[“problem_id”] , $row[“firstname”].” “.$row[“lastname”] ) , $row[“summary”] , $row[“source”] , $row[“status”]

); } if ($calls > 0) {

// there was at least one call, so the table was opened - // close it. print end_table();

CREATE_PROBLEM _CODE( ) This function creates a unique and highly random 8-character alphanumeric code.

function create_problem_code() {

return substr(md5(uniqid(rand())),0,8); }

354 Part IV: Not So Simple Applications