Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 48 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
48
Dung lượng
1,21 MB
Nội dung
need to pass text values to htmlentities() to avoid problems with displaying quotes. Display $title in the value attribute of the title input field like this: <input name="title" type="text" class="widebox" id="title" ➥ value="<?php echo htmlentities($title); ?>" /> 5. Do the same for the article text area. Because text areas don’t have a value attrib- ute, the code goes between the opening and closing <textarea> tags like this: <textarea name="article" cols="60" rows="8" class="widebox" ➥ id="article"><?php echo htmlentities($article); ?></textarea> Make sure there is no space between the opening and closing PHP and <textarea> tags. Otherwise, you will get unwanted spaces in your updated record. 6. The UPDATE command needs to know the primary key of the record you want to change. You need to store the primary key in a hidden field so that it is submitted in the $_POST array with the other details. Because hidden fields are not displayed onscreen, the following code can go anywhere inside the form: <input name="article_id" type="hidden" value="<?php ➥ echo $article_id; ?>" /> 7. Save the update page, and test it by loading journal_list.php into a browser and selecting the EDIT link for one of the records. The contents of the record should be displayed in the form fields as shown in Figure 13-4. The Update entry button doesn’t do anything yet. Just make sure that everything is displayed correctly, and confirm that the primary key is registered in the hidden field. You can check your code, if necessary, against journal_update_mysqli01.php. 8. The name attribute of the submit button is update, so all the update processing code needs to go in a conditional statement that checks for the presence of update in the $_POST array. Place the following code highlighted in bold immediately above the code in step 1 that redirects the page: $stmt->fetch(); } } // if form has been submitted, update record if (array_key_exists('update', $_POST)) { // prepare update query $sql = 'UPDATE journal SET title = ?, article = ? WHERE article_id = ?'; // initialize statement $stmt = $conn->stmt_init(); if ($stmt->prepare($sql)) { $stmt->bind_param('ssi', $_POST['title'], $_POST['article'], ➥ $_POST['article_id']); $done = $stmt->execute(); } } MANAGING CONTENT 367 13 7311ch13.qxd 10/10/06 10:58 PM Page 367 // redirect page on success or if $_GET['article_id']) not defined if ($done || !isset($_GET['article_id'])) { This is very similar to the INSERT query. The UPDATE query is prepared with question mark placeholders where values are to be supplied from variables. You then initial- ize the statement and bind the variables with $stmt->bind_param(). The first two variables are strings, and the third is an integer, so the first argument is 'ssi'. If the UPDATE query succeeds, $done is set to true. You need to add $done || to the condition in the redirect script. This ensures that the page is redirected either if the update succeeds or if someone tries to access the page directly. 9. Save journal_update.php and test it by loading journal_list.php, selecting one of the EDIT links, and making changes to the record that is displayed. When you click Update record, you should be taken back to journal_list.php. You can verify that your changes were made by clicking the same EDIT link again. Check your code, if necessary, with journal_update_mysqli02.php. Use journal_update01.php from the download files. The code for the first stage of the update process is in journal_update_pdo01php, and the final code is in journal_update_pdo02.php. 1. The first stage involves retrieving the details of the record that you want to update. Put the following code above the DOCTYPE declaration: <?php include(' /includes/conn_pdo.inc.php'); include(' /includes/corefuncs.php'); // remove backslashes nukeMagicQuotes(); // initialize flags $OK = false; $done = false; // create database connection $conn = dbConnect('admin'); // get details of selected record if (isset($_GET['article_id']) && !$_POST) { // prepare SQL query $sql = 'SELECT * FROM journal WHERE article_id = ?'; $stmt = $conn->prepare($sql); // execute query by passing array of variables $OK = $stmt->execute(array($_GET['article_id'])); // fetch the result $row = $stmt->fetch(); // assign result array to variables if (is_array($row)) { extract($row); } } PHP Solution 13-7: Updating a record with PDO PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 368 7311ch13.qxd 10/10/06 10:58 PM Page 368 // redirect if $_GET['article_id'] not defined if (!isset($_GET['article_id'])) { header('Location: http://localhost/phpsolutions/admin/ ➥ journal_list.php'); exit; } // display error message if query fails if (isset($stmt) && !$OK && !$done) { $error = $stmt->errorInfo(); if (isset($error[2])) { echo $error[2]; } } ?> Although this is very similar to the code used for the insert page, the first few lines are outside the first conditional statement. Both stages of the update process require the include files, the removal of backslashes, and the database connection, so this avoids the need to duplicate the same code later. Two flags are initialized: $OK to check the success of retrieving the record and $done to check whether the update succeeds. The first conditional statement checks that $_GET ['article_id'] exists and that the $_POST array is empty. This makes sure that the code inside is executed only when the query string is set, but the form hasn’t been submitted. When preparing the SQL query for the insert form, you used named placeholders for the variables. This time, let’s use a question mark like this: $sql = 'SELECT * FROM journal WHERE article_id = ?'; When using question marks as placeholders, you pass the variables directly as an array to $stmt->execute() like this: $OK = $stmt->execute(array($_GET['article_id'])); Even though there is only one variable this time, it must still be presented as an array. There’s only one record to fetch in the result, so it’s stored immediately in $row, and the extract() function is used to assign the values to simple variables. (See “A quick warning about extract()” later in this chapter for a description of how it works.) If someone alters the query string and searches for a nonexistent record, $row will be empty, so it’s necessary to test it with is_array() before passing it to extract(). The next conditional statement redirects the page to journal_list.php if $_GET['article_id'] hasn’t been defined. This prevents anyone from trying to load the update page directly in a browser. The final conditional statement displays an error message if the prepared state- ment has been created, but both $OK and $done remain false. You haven’t added the update script yet, but if the record is retrieved or updated successfully, one of them will be switched to true. So if both remain false, you know there was some- thing wrong with one of the SQL queries. MANAGING CONTENT 369 13 7311ch13.qxd 10/10/06 10:58 PM Page 369 2. Now that you have retrieved the contents of the record, you need to display them in the update form by using PHP to populate the value attribute of each input field. Before doing so, it’s a good idea to check that you actually have something to display. Add the following conditional statement immediately before the opening <form> tag: <p><a href="journal_list.php">List all entries</a> </p> <?php if (!isset($article_id)) { ?> <p class="warning">Invalid request: record does not exist.</p> <?php } else { ?> <form id="form1" name="form1" method="post" action=""> If someone changes the value of article_id in the query string, you may get an empty result set, so extract() cannot create $article_id. In that event, there’s no point in going any further, and a warning message is displayed. The form is wrapped in the else clause and displayed only if the query finds a valid record. Add the closing curly brace of the else clause immediately after the closing </form> tag like this: </form> <?php } ?> </body> 3. If $article_id has been defined, you know that $title and $article also exist and can be displayed in the update form without further testing. However, you need to pass text values to htmlentities() to avoid problems with displaying quotes. Display $title in the value attribute of the title input field like this: <input name="title" type="text" class="widebox" id="title" ➥ value="<?php echo htmlentities($title); ?>" /> 4. Do the same for the article text area. Because text areas don’t have a value attrib- ute, the code goes between the opening and closing <textarea> tags like this: <textarea name="article" cols="60" rows="8" class="widebox" ➥ id="article"><?php echo htmlentities($article); ?></textarea> Make sure there is no space between the opening and closing PHP and <textarea> tags. Otherwise, you will get unwanted spaces in your updated record. 5. The UPDATE command needs to know the primary key of the record you want to change. You need to store the primary key in a hidden field so that it is submitted in the $_POST array with the other details. Because hidden fields are not displayed onscreen, the following code can go anywhere inside the form: <input name="article_id" type="hidden" value="<?php ➥ echo $article_id; ?>" /> 6. Save the update page, and test it by loading journal_list.php into a browser and selecting the EDIT link for one of the records. The contents of the record should be displayed in the form fields as shown in Figure 13-4. The Update entry button doesn’t do anything yet. Just make sure that everything is displayed correctly, and confirm that the primary key is registered in the hidden field. You can check your code, if necessary, against journal_update_pdo01.php. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 370 7311ch13.qxd 10/10/06 10:58 PM Page 370 7. The name attribute of the submit button is update, so all the update processing code needs to go in a conditional statement that checks for the presence of update in the $_POST array. Place the following code highlighted in bold immediately above the code in step 1 that redirects the page: extract($row); } // if form has been submitted, update record if (array_key_exists('update', $_POST)) { // prepare update query $sql = 'UPDATE journal SET title = ?, article = ? WHERE article_id = ?'; $stmt = $conn->prepare($sql); // execute query by passing array of variables $done = $stmt->execute(array($_POST['title'], $_POST['article'], ➥ $_POST['article_id'])); } // redirect page on success or $_GET['article_id'] not defined if ($done || !isset($_GET['article_id'])) { Again, the SQL query is prepared using question marks as placeholders for values to be derived from variables. This time there are three placeholders, so the corre- sponding variables need to be passed as an array to $stmt->execute(). Needless to say, the array must be in the same order as the placeholders. If the UPDATE query succeeds, $done is set to true. You need to add $done || to the condition in the redirect script. This ensures that the page is redirected either if the update succeeds or if someone tries to access the page directly. 8. Save journal_update.php and test it by loading journal_list.php, selecting one of the EDIT links, and making changes to the record that is displayed. When you click Update record, you should be taken back to journal_list.php. You can verify that your changes were made by clicking the same EDIT link again. Check your code, if necessary, with journal_update_pdo02.php. Deleting records Deleting a record in a database is similar to updating one. The basic DELETE command looks like this: DELETE FROM table_name WHERE condition What makes the DELETE command potentially dangerous is that it is final. Once you have deleted a record, there’s no going back—it’s gone forever. There’s no Recycle Bin or Trash to fish it out from. Even worse, the WHERE clause is optional. If you omit it, every single record in the table is irrevocably sent into cyber-oblivion. Consequently, it’s a good idea to use PHP logic to display details of the record to be deleted, and ask the user to confirm or cancel the process (see Figure 13-5). MANAGING CONTENT 371 13 7311ch13.qxd 10/10/06 10:58 PM Page 371 Figure 13-5. Deleting a record is irreversible, so it’s a good idea to get confirmation before going ahead. Building and scripting the delete page is almost identical to the update page, so I won’t give step-by-step instructions. However, here are the main points: Retrieve the details of the selected record. Display sufficient details, such as the title, for the user to confirm that the correct record has been selected. Give the Confirm deletion and Cancel buttons different name attributes, and use each name attribute in array_key_exists() to control the action taken. Instead of wrapping the entire form in the else clause, use conditional statements to hide the Confirm deletion button and the hidden field. The code that performs the deletion for each method follows. For the original MySQL extension: if (array_key_exists('delete', $_POST)) { if (!is_numeric($_POST['article_id'])) { die('Invalid request'); } $sql = "DELETE FROM journal WHERE article_id = {$_POST['article_id']}"; $deleted = mysql_query($sql) or die(mysql_error()); } For MySQL Improved: if (array_key_exists('delete', $_POST)) { $sql = 'DELETE FROM journal WHERE article_id = ?'; $stmt = $conn->stmt_init(); The curly braces around $_POST['article_id'] are needed even though the variable uses single quotes. This is a quirk of PHP. Elements of associative arrays (ones that use a string as the array key) need special treatment inside double-quoted strings. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 372 7311ch13.qxd 10/10/06 10:58 PM Page 372 if ($stmt->prepare($sql)) { $stmt->bind_param('i', $_POST['article_id']); $deleted = $stmt->execute(); } } For PDO: if (array_key_exists('delete', $_POST)) { $sql = 'DELETE FROM journal WHERE article_id = ?'; $stmt = $conn->prepare($sql); $deleted = $stmt->execute(array($_POST['article_id'])); } You can find the finished code in journal_delete_mysql.php, journal_delete_mysqli.php, and journal_delete_pdo.php. A quick warning about extract() PHP Solution 13-7 employs a function called extract() to create shorter variables from the results of a database query. It’s a very useful function, but since it appears in a section that you might not read unless you’re using PDO, it deserves to be described separately. Take the following associative array: $book = array('author' => 'David Powers', 'title' =>'PHP Solutions'); Pass the array to extract() like this: extract($book); This creates a variable from each array key with the same value as the equivalent array ele- ment. In other words, you get $author with the value David Powers, and $title with the value PHP Solutions. This is so convenient that it’s tempting to use extract() to convert the contents of the $_POST and $_GET arrays into the equivalent variables. It works, but is fraught with danger, because the default behavior is to overwrite existing variables of the same name. It’s far safer to process $_POST and $_GET variables individually or by testing for them in an array of expected items, as shown earlier in this chapter. You can influence the behavior of extract() by using a constant as the second argument. For instance, EXTR_PREFIX_ALL prefixes all variables with a string supplied as the third argument like this: extract($book, EXTR_PREFIX_ALL, 'php'); This produces two variables called $php_author and $php_title. MANAGING CONTENT 373 13 7311ch13.qxd 10/10/06 10:58 PM Page 373 The extract() function generates a PHP warning if you pass it anything other than an array, so it’s always a good idea to test any argument with is_array() first like this: if (is_array($myVariable)) { extract($myVariable); } You should also test for the existence of any variables you expect to be created by extract(). Alternatively, set a Boolean flag to true or false depending on the outcome of the is_array() test. To find out more about extract() and the other constants you can use with it, see www.php.net/manual/en/function.extract.php. Reviewing the four essential SQL commands Now that you have seen SELECT, INSERT, UPDATE, and DELETE in action, let’s review the basic syntax. This is not an exhaustive listing, but it concentrates on the most important options, including some that have not yet been covered. I have used the same typographic conventions as the MySQL online manual at http://dev.mysql.com/doc/refman/5.0/en (which you may also want to consult): Anything in uppercase is a SQL command. Expressions in square brackets are optional. Lowercase italics represent variable input. A vertical pipe (|) separates alternatives. Although some expressions are optional, they must appear in the order listed. For exam- ple, in a SELECT query, WHERE, ORDER BY, and LIMIT are all optional; but LIMIT can never come before WHERE or ORDER BY. SELECT SELECT is used for retrieving records from one or more tables. Its basic syntax is as follows: SELECT [DISTINCT] select_list FROM table_list [WHERE where_expression] [ORDER BY col_name | formula] [ASC | DESC] [LIMIT [skip_count,] show_count] The DISTINCT option tells the database you want to eliminate duplicate rows from the results. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 374 7311ch13.qxd 10/10/06 10:58 PM Page 374 The select_list is a comma-separated list of columns that you want included in the result. To retrieve all columns, use an asterisk (*). The asterix shorthand must always be used on its own; it cannot be combined with other column names. For example, you cannot use it with an alias (see “Extracting a fixed number of characters” in the next chap- ter) to mean “all other columns.” If the same column name is used in more than one table, you must use unambiguous references by using the syntax table_name.column_name. The table_list is a comma-separated list of tables from which the results are to be drawn. All tables that you want to be included in the results must be listed. The WHERE clause specifies search criteria. For example: WHERE quotations.family_name = authors.family_name WHERE quotations.author_id = 32 WHERE expressions can use comparison, arithmetic, logical, and pattern-matching opera- tors. The most important ones are listed in Table 13-2. MANAGING CONTENT 375 13 Table 13-2. The main operators used in MySQL WHERE expressions Comparison Arithmetic < Less than + Addition <= Less than or equal to - Subtraction = Equal to * Multiplication != Not equal to / Division > Greater than DIV Integer division >= Greater than or equal to % Modulo IN() Included in list Logical Pattern matching AND Logical and LIKE Case-insensitive match && Logical and NOT LIKE Case-insensitive nonmatch OR Logical or LIKE BINARY Case-sensitive match || Logical or (best avoided) NOT LIKE BINARY Case-sensitive nonmatch Between (and including) two values BETWEEN min AND max 7311ch13.qxd 10/10/06 10:58 PM Page 375 DIV is the counterpart of the modulo operator. It produces the result of division as an inte- ger with no fractional part, whereas modulo produces only the remainder. 5 / 2 /* result 2.5 */ 5 DIV 2 /* result 2 */ 5 % 2 /* result 1 */ I suggest you avoid using || because it has a completely different meaning in standard SQL. By not using it with MySQL, you avoid confusion if you ever work with a different relational database. IN() evaluates a comma-separated list of values inside the parentheses and returns true if one or more of the values is found. Although BETWEEN is normally used with numbers, it also applies to strings. For instance, BETWEEN 'a' AND 'd' returns true for a, b, c, and d (but not their uppercase equivalents). Both IN() and BETWEEN can be preceded by NOT to perform the opposite comparison. LIKE, NOT LIKE, and the related BINARY operators are used for text searches in combina- tion with the following two wildcard characters: %: matches any sequence of characters or none. _ (an underscore): matches exactly one character. So, the following WHERE clause matches Dennis, Denise, and so on, but not Aiden: WHERE first_name LIKE 'den%' To match Aiden, put % at the front of the search pattern. Because % matches any sequence of characters or none, '%den%' still matches Dennis and Denise. To search for a literal per- centage sign or underscore, precede it with a backslash (\% or \_). Conditions are evaluated from left to right, but can be grouped in parentheses if you want a particular set of conditions to be considered together. ORDER BY specifies the sort order of the results. This can be specified as a single column, a comma-separated list of columns, or an expression such as RAND(), which randomizes the order. The default sort order is ascending (a–z, 0–9), but you can specify DESC (descending) to reverse the order. LIMIT followed by one number stipulates the maximum number of records to return. If two numbers are given separated by a comma, the first tells the database how many rows to skip (see “Selecting a subset of records” in Chapter 12). For more details on SELECT, see http://dev.mysql.com/doc/refman/5.0/en/select.html. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 376 7311ch13.qxd 10/10/06 10:58 PM Page 376 [...]... leading zero if necessary 00 through 59 Seconds s %S Seconds with leading zero if necessary 00 through 59 AM/PM a %p Lowercase am AM/PM A Uppercase PM * Note: %e is not supported on Windows 14 395 7311ch14.qxd 10/10/06 11:03 PM Page 396 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY You can combine these format characters with punctuation to display the current date in your web pages according to your own preferences... However, replacing the slashes with hyphens in the first example, as follows, produces a false result: $notXmas = strtotime('12-25-2006'); // produces Dec 31, 196 9 timestamp 393 7311ch14.qxd 10/10/06 11:03 PM Page 394 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY To avoid such problems, it’s best to use the name of the month, either spelled out in full or just the first three letters, and to place the year at... 10/10/06 11:03 PM Page 384 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 100 characters, use either of the preceding methods to start with, and store the result in $extract Then you can use the PHP string functions strrpos() and substr() to find the last space and end the extract like this (the code is in journal_word_mysql .php, journal_word_mysqli .php, and journal_word_pdo .php) : $extract = $row['first100'];... updated in the past week 391 7311ch14.qxd 10/10/06 11:03 PM Page 392 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 3 Save and reload the page in your browser Depending on when you last updated an item in the journal table, you should see nothing or a limited range of items If necessary, change the interval type to DAY or HOUR to test that the time limit is working 4 Open journal_list .php, select an item that... instead Although date_converter .php just displays the result, when adapting the code for an insert form, for example, use the tests like this: if (isset($error)) { // abandon insertion of data and display error messages } elseif (isset($mysqlFormat)) { // go ahead with insertion of data } 14 399 7311ch14.qxd 10/10/06 11:03 PM Page 400 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY Working with multiple database... are assumed There are a lot of format characters Some are easy to remember, but many seem to have no obvious reasoning behind them You can find a full list at www .php. net/manual/en/ function.date .php and www .php. net/manual/en/function.strftime .php Table 14-3 lists the most useful 394 7311ch14.qxd 10/10/06 11:03 PM Page 395 SOLUTIONS TO COMMON PHP/ MYSQL PROBLEMS Table 14-3 The main format characters... perhaps in the case of the following example, dream about): UPDATE sales SET q1_2007 = 25000 WHERE title = 'PHP Solutions' 13 For more details on UPDATE, see http://dev.mysql.com/doc/refman/5.0/en/update.html 377 7311ch13.qxd 10/10/06 10:58 PM Page 378 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY DELETE DELETE can be used to delete single records, multiple records, or the entire contents of a table... If the values of $i and $thisMonth are the same, the if statement inserts selected="selected" into the tag The final part of the script displays the 397 7311ch14.qxd 10/10/06 11:03 PM Page 398 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY name of the month by drawing it from the $months array Because indexed arrays begin at 0, you need to subtract 1 from the value of $i to get the right month... 12-hour clock without leading zero 1, 11 Minutes %i With leading zero 05, 25 Seconds %S With leading zero 08, 45 AM/PM %p Month Day of month Weekday name Hour 14 3 89 7311ch14.qxd 10/10/06 11:03 PM Page 390 PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY As explained earlier, when using a function in a SQL query, assign the result to an alias using the AS keyword Referring to Table 14-1, you can now format... create a link to a page containing the full text PHP Solution 14-1: Displaying the first two sentences of an article If you created the Japan Journey site earlier in the book, use journal .php Alternatively, use journal01 .php from the download files for this chapter, and copy it to the phpsolutions site root You also need footer.inc .php, menu.inc .php, title.inc .php, and the correct MySQL connection file in . only in PHP 5 and above. Both func- tions also have case-insensitive versions. To find out more, visit www .php. net/manual/ en/function.strpos .php. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 384 7311ch14.qxd. hidden field. You can check your code, if necessary, against journal_update_pdo01 .php. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 370 7311ch13.qxd 10/10/06 10:58 PM Page 370 7. The name attribute of. a quirk of PHP. Elements of associative arrays (ones that use a string as the array key) need special treatment inside double-quoted strings. PHP SOLUTIONS: DYNAMIC WEB DESIGN MADE EASY 372 7311ch13.qxd