Giải pháp thiết kế web động với PHP - p 47 ppsx

10 214 0
Giải pháp thiết kế web động với PHP - p 47 ppsx

Đang tải... (xem toàn văn)

Thông tin tài liệu

MANAGING MULTIPLE DATABASE TABLES 441 Figure 16-8. The multiple-choice <select> list pulls the values from the categories table. 7. View the pages source code to verify that the primary key of each category is correctly embedded in the value attribute of each <option> tag. You can compare your code with blog_insert_mysqli_01.php in the ch16 folder. 8. Next, create the <select> drop-down menu to display the images already registered in the database. Add this code immediately after the code you inserted in step 5: <p> <label for="image_id">Uploaded image:</label> <select name="image_id" id="image_id"> <option value="">Select image</option> <?php // get the list of images $getImages = 'SELECT image_id, filename FROM images ORDER BY filename'; $images = $conn->query($getImages); while ($row = $images->fetch_assoc()) { ?> <option value="<?php echo $row['image_id']; ?>" <?php if (isset($_POST['image_id']) && $row['image_id'] == $_POST['image_id']) { echo 'selected'; } ?>><?php echo $row['filename']; ?></option> <?php } ?> </select> </p> CHAPTER 16 442 This creates another SELECT query to get the primary key and filename of each image stored in the images table. The code should be very familiar by now, so it needs no explanation. 9. The check box, file input field, and text input field for the caption go between the code in the previous step and the submit button. The code looks like this: <p id="allowUpload"> <input type="checkbox" name="upload_new" id="upload_new"> <label for="upload_new">Upload new image</label> </p> <p class="optional"> <label for="image">Select image:</label> <input type="file" name="image" id="image"> </p> <p class="optional"> <label for="caption">Caption:</label> <input name="caption" type="text" class="widebox" id="caption"> </p> The paragraph that contains the check box has been given the ID allowUpload, and the two other paragraphs have been assigned a class called optional. The style rules in admin.css set the display property of these three paragraphs to none. 10. Save blog_insert_mysqli.php, and load the page in a browser. The images <select> drop- down menu is displayed below the categories list, but the three form elements you inserted in step 9 are hidden. This is what will be displayed if JavaScript is disabled in the browser. Users will have the option to select categories and an existing image but not to upload a new image. If necessary, check your code against blog_insert_mysqli_02.php in the ch16 folder. 11. Copy toggle_fields.js from the ch16 folder to the admin folder. The file contains the following JavaScript: var cbox = document.getElementById('allowUpload'); cbox.style.display = 'block'; var uploadImage = document.getElementById('upload_new'); uploadImage.onclick = function () { var image_id = document.getElementById('image_id'); var image = document.getElementById('image'); var caption = document.getElementById('caption'); var sel = uploadImage.checked; image_id.disabled = sel; image.parentNode.style.display = sel ? 'block' : 'none'; caption.parentNode.style.display = sel ? 'block' : 'none'; image.disabled = !sel; caption.disabled = !sel; } This uses the IDs of the elements inserted in step 8 to control their display. If JavaScript is enabled, the check box is automatically displayed when the page loads, but the file input field and text input field for the caption remain hidden. If the check box is checked, the drop-down Download from Wow! eBook <www.wowebook.com> MANAGING MULTIPLE DATABASE TABLES 443 menu of existing images is disabled, and the hidden elements are displayed. If the check box is subsequently unchecked, the drop-down menu is reenabled, and the file input field and caption field are hidden again. JavaScript is beyond the scope of this book, but you can learn more from Getting StartED with JavaScript by Terry McNavage (friends of ED, 2010, ISBN: 978-1-4302-7219-9) and DOM Scripting: Web Design with JavaScript and the Document Object Model, Second Edition by Jeremy Keith (friends of ED, 2010, ISBN: 978-1-4302-3389-3). 12. Link toggle_fields.js to blog_insert_mysqli.php with a <script> tag just before the closing </body> tag like this: </form> <script src="toggle_fields.js"></script> </body> Although it has been common practice for many years to put <script> tags for external JavaScript files in the <head> of a web page, more recent practice recommends adding most scripts as close to the bottom of the <body> as possible to speed up downloading and display. The code in toggle_fields.js wont work correctly if you add it to the <head>. 13. Save blog_insert_mysqli.php, and load the page in a browser. In a JavaScript-enabled browser, the check box should be displayed between the <select> drop-down menu and submit button. Select the check box to disable the drop-down menu and display the hidden fields, as shown in Figure 16-9. Figure 16-9. The check box controls the display of the file and caption input fields. CHAPTER 16 444 14. Deselect the check box. The file and caption input fields are hidden, and the drop-down menu is reenabled. You can check your code, if necessary with blog_insert_mysqli_03.php and toggle_fields.js in the ch16 folder. If youre wondering why I used JavaScript, rather than PHP, to control the display of the file and caption input fields, its because PHP is a server-side language. After the PHP engine has sent the output to the browser, it has no further interaction with the page unless you send another request to the web server. JavaScript, on the other hand, works in the browser, so its able to manipulate the content of the page locally. JavaScript can also be used in conjunction with PHP to send requests to the web server in the background and can use the result to refresh part of the page without reloading it—a technique known as Ajax, which is beyond the scope of this book. The updated insert form now has input fields for categories and images, but the processing script still handles only the text input field for the title and the text area for the blog entry. PHP Solution 16-4: Inserting data into multiple tables This PHP solution adapts the existing script in blog_insert_mysqli.php to upload a new image (if required) and insert data into the images, blog, and article2cat tables following the decision chain outlined in Figure 16-6. It assumes you have set up the article2cat cross-reference table and have completed PHP Solutions 16-2 and 16-3. Dont attempt to rush through this section. The code is quite long, but it brings together many of the techniques you have learned previously. 1. In the conditional statement at the top of blog_insert_mysqli.php locate the following code highlighted in bold: if (isset($_POST['insert'])) { // initialize flag $OK = false; // initialize prepared statement $stmt = $conn->stmt_init(); 2. Immediately after the highlighted code, insert some space to add the following conditional statement to process the image, if one has been uploaded or selected. // initialize prepared statement $stmt = $conn->stmt_init(); // if a file has been uploaded, process it if(isset($_POST['upload_new']) && $_FILES['image']['error'] == 0) { $imageOK = false; require_once(' /classes/Ps2/Upload.php'); $upload = new Ps2_Upload(' /images/'); $upload->move(); $names = $upload->getFilenames(); // $names will be an empty array if the upload failed if ($names) { $sql = 'INSERT INTO images (filename, caption) VALUES (?, ?)'; MANAGING MULTIPLE DATABASE TABLES 445 $stmt->prepare($sql); $stmt->bind_param('ss', $names[0], $_POST['caption']); $stmt->execute(); $imageOK = $stmt->affected_rows; } // get the image's primary key or find out what went wrong if ($imageOK) { $image_id = $stmt->insert_id; } else { $imageError = implode(' ', $upload->getMessages()); } } elseif (isset($_POST['image_id']) && !empty($_POST['image_id'])) { // get the primary key of a previously uploaded image $image_id = $_POST['image_id']; } // create SQL $sql = 'INSERT INTO blog (title, article, created) VALUES(?, ?, NOW())'; This begins by checking if $_POST['upload_new'] has been set. As explained in Chapter 5, a check box is included in the $_POST array only if it has been selected. So, if the check box hasnt been selected, the condition fails, and the elseif clause at the bottom is tested instead. The elseif clause checks for the existence of $_POST['image_id']. If it exists and is not empty, an existing image has been selected from the drop-down menu, and the value is stored in $image_id. If both tests fail, an image has neither been uploaded nor selected from the drop-down menu. The script later takes this into account when preparing the INSERT query for the blog table, allowing you to create a blog entry without an image. However, if $_POST['upload_new'] exists, the check box has been selected, and an image has probably been uploaded. To make sure, the conditional statement also checks the value of $_FILES['image']['error']. As you learned in Chapter 6, the error code 0 indicates a successful upload. Any other error code means the upload failed or that no file was selected. Assuming a file has been successfully uploaded from the form, the conditional statement includes the Ps2_Upload class and creates an upload object, setting the destination folder to images. It then calls the move() method to move the file to the images folder. To avoid complicating the code, Im using the default maximum size and MIME types. The changes you made to the Ps2_Upload class in PHP Solution 16-2 add the name of an uploaded file to the $_filenames property only if the file was moved successfully to the destination folder. The getFilenames() method retrieves the contents of the $_filenames property, and assigns the result to $names. If the file was moved successfully, its filename is stored as the first element of the $names array. So if $names contains a value, you can safely proceed with the INSERT query, which binds the values of $names[0] and $_POST['caption'] as strings to the prepared statement. CHAPTER 16 446 After the statement has been executed, the affected_rows property resets the value of $imageOK. If the INSERT query succeeded, $imageOK is 1, which is treated as true. If the image details were inserted in the images table, the insert_id property retrieves the primary key of the new record and stores it in $image_id. The insert_id property must be accessed before running any other SQL queries, because it contains the primary key of the most recent query. However, if $imageOK is still false, the getMessages() method of the upload object is called, and the result is stored in $imageError. The getMessages() method returns an array, so the implode() function is used to join the array elements as a single string. The most likely causes of failure are a file thats too big or of the wrong MIME type. 3. As long as the image upload didnt fail, the next stage in the process is to insert the blog entry into the blog table. The form of the INSERT query depends on whether an image is associated with the blog entry. If it is, $image_id exists and needs to be inserted in the blog table as a foreign key. Otherwise, the original query can be used. Amend the original query like this: // don't insert blog details if the image failed to upload if (!isset($imageError)) { // if $image_id has been set, insert it as a foreign key if (isset($image_id)) { $sql = 'INSERT INTO blog (image_id, title, article, created) VALUES(?, ?, ?, NOW())'; $stmt->prepare($sql); $stmt->bind_param('iss', $image_id, $_POST['title'], $_POST['article']); } else { // create SQL $sql = 'INSERT INTO blog (title, article, created) VALUES(?, ?, NOW())'; $stmt->prepare($sql); $stmt->bind_param('ss', $_POST['title'], $_POST['article']); } // execute and get number of affected rows $stmt->execute(); $OK = $stmt->affected_rows; } This whole section of code is wrapped in a conditional statement that checks whether $imageError exists. If it does, theres no point in inserting the new blog entry, so the entire code block is ignored. However, if $imageError doesnt exist, the nested conditional statement prepares different INSERT queries depending on whether $image_id exists and then executes whichever one has been prepared. 4. The next stage of the process inserts values into the article2cat cross-reference table. The code follows immediately after the code in the previous step and looks like this: MANAGING MULTIPLE DATABASE TABLES 447 // if the blog entry was inserted successfully, check for categories if ($OK && isset($_POST['category'])) { // get the article's primary key $article_id = $stmt->insert_id; foreach ($_POST['category'] as $cat_id) { if (is_numeric($cat_id)) { $values[] = "($article_id, " . (int) $cat_id . ')'; } } if ($values) { $sql = 'INSERT INTO article2cat (article_id, cat_id) VALUES ' . implode(',', $values); // execute the query and get error message if it fails if (!$conn->query($sql)) { $catError = $conn->error; } } } The value of $OK is determined by the affected_rows property from the query that inserted the data in the blog table, and the multiple-choice <select> list is included in the $_POST array only if any categories are selected. So, this code block is run only if the data was successfully inserted in the blog table and at least one category was selected in the form. It begins by obtaining the primary key of the insert operation from the prepared statements insert_id property and assigning it to $article_id. The form submits the category values as an array. The foreach loop checks each value in $_POST['category']. If the value is numeric, the following line is executed: $values[] = "($article_id, " . (int) $cat_id . ')'; This creates a string with the two primary keys, $article_id and $cat_id, separated by a comma and wrapped in a pair of parentheses. The (int) casting operator makes sure that $cat_id is an integer. The result is assigned to an array called $values. For example, if $article_id is 10 and $cat_id is 4, the resulting string assigned to the array is (10, 4). If $values contains any elements, implode() converts it to a comma-separated string and appends it to the SQL query. For example, if categories 2, 4, and 5 are selected, the resulting query looks like this: INSERT INTO article2cat (article_id, cat_id) VALUES (10, 2),(10, 4),(10,5) As explained in “Reviewing the four essential SQL commands” in Chapter 13, this is how you insert multiple rows with a single INSERT query. Because $article_id comes from a reliable source and the data type of $cat_id has been checked, its safe to use these variables directly in a SQL query without using a prepared statement. The query is executed with the query() method. If it fails, the connection objects error property is stored in $catError. CHAPTER 16 448 5. The final section of code handles the redirect on success and error messages. The amended code looks like this: // redirect if successful or display error if ($OK && !isset($imageError) && !isset($catError)) { header('Location: http://localhost/phpsols/admin/blog_list_mysqli.php'); exit; } else { $error = $stmt->error; if (isset($imageError)) { $error .= ' ' . $imageError; } if (isset($catError)) { $error .= ' ' . $catError; } } } The condition controlling the redirect now makes sure that $imageError and $catError dont exist. If either does, the value is concatenated to the original $error, which contains any error message from the prepared statement object. 6. Save blog_insert_mysqli.php, and test it in a browser. Try uploading an image thats too big or a file of the wrong MIME type. The form should be redisplayed with an error message and the blog details preserved. Also try inserting blog entries with and without images and/or categories. You now have a versatile insert form. If you dont have suitable images to upload, use the images in the phpsols images folder. The Ps2_Upload class renames them to avoid overwriting the existing files. You can check your code against blog_insert_mysqli_04.php in the ch16 folder. The PDO version is in blog_insert_pdo.php in the ch16 folder. PDO uses the lastInsertId() method on the connection object to get the primary key of the most recent insert operation. For example, the following line gets the primary key of the blog entry: $article_id = $conn->lastInsertId(); Like the MySQLi insert_id property, you need to access it immediately after the INSERT query has been executed. Updating and deleting records in multiple tables The addition of the categories and article2cat tables means that the changes you made to blog_update_mysqli.php in PHP Solution 15-2 in the previous chapter no longer adequately cover the foreign key relationships in the phpsols database. In addition to amending the update form, you also need to create scripts to delete records without destroying the databases referential integrity. MANAGING MULTIPLE DATABASE TABLES 449 Updating records in a cross-reference table Each record in a cross-reference table contains only a composite primary key. Normally, primary keys should never be altered. Moreover, they must be unique. This poses a problem for updating the article2cat table. If you make no changes to the selected categories when updating a blog entry, the cross-reference table doesnt need to be updated. However, if the categories are changed, you need to work out which cross references to delete and which new ones to insert. Rather than getting tied up in knots working out whether any changes have been made, a simple solution is to delete all existing cross references and insert the selected categories again. If no changes have been made, you simply insert the same ones again. PHP Solution 16-5: Adding categories to the update form This PHP solution amends blog_update_mysqli.php from PHP Solution 15-2 in the previous chapter to allow you to update the categories associated with a blog entry. To keep the structure simple, the only change that can be made to the image associated with the entry is to select a different existing image or no image at all. 1. Continue working with blog_update_mysqli.php from PHP Solution 15-2. Alternatively, copy blog_update_mysqli_04.php from the ch16 folder, and save it in the admin folder as blog_update_mysqli.php. 2. When the page first loads, you need to run a second query to get the categories associated with the blog entry. Add the following highlighted code to conditional statement that gets details of the selected record: $stmt->free_result(); // get categories associated with the article $sql = 'SELECT cat_id FROM article2cat WHERE article_id = ?'; $stmt->prepare($sql); $stmt->bind_param('i', $_GET['article_id']); $stmt->bind_result($cat_id); $OK = $stmt->execute(); // loop through the results to store them in an array $selected_categories = array(); while ($stmt->fetch()) { $selected_categories[] = $cat_id; } } The query selects cat_id from all records in the cross-reference table that match the primary key of the selected blog entry. The results are bound to $cat_id, and a while loop extracts the values into an array called $selected_categories. 3. In the body of the HTML page, add a multiple-choice <select> list between the text area and the <select> drop-down menu that displays the list of images. Use another SQL query to populate it like this: CHAPTER 16 450 <p> <label for="category">Categories:</label> <select name="category[]" size="5" multiple id="category"> <?php // get categories $getCats = 'SELECT cat_id, category FROM categories ORDER BY category'; $categories = $conn->query($getCats); while ($row = $categories->fetch_assoc()) { ?> <option value="<?php echo $row['cat_id']; ?>" <?php if (in_array($row['cat_id'], $selected_categories)) { echo 'selected'; } ?>><?php echo $row['category']; ?></option> <?php } ?> </select> </p> The while loop builds each <option> tag, inserting cat_id in the value attribute and displaying the category between the opening and closing tags. If cat_id is in the $selected_categories array, selected is inserted in the <option> tag. This selects the categories already associated with the blog entry. 4. Save blog_update_mysqli.php, and select one of the EDIT links in blog_list_mysqli.php to make sure the multiple-choice list is populated with the categories. If you inserted a new entry in PHP Solution 16-4, the categories you associated with the item should be selected, as shown in the following screenshot. You can check your code, if necessary, against blog_update_mysqli_05.php in the ch16 folder. The PDO version is in blog_update_pdo_05.php. 5. Next, you need to edit the section of code that updates the record when the form is submitted. The code currently looks like this: // if form has been submitted, update record if (isset($_POST ['update'])) { // prepare update query if (!empty($_POST['image_id'])) { $sql = 'UPDATE blog SET image_id = ?, title = ?, article = ? WHERE article_id = ?'; $stmt->prepare($sql); $stmt->bind_param('issi', $_POST['image_id'], $_POST['title'],  $_POST['article'], $_POST['article_id']); . ?'; $stmt->prepare($sql); $stmt->bind_param('issi', $_POST['image_id'], $_POST['title'],  $_POST['article'], $_POST['article_id']);. you simply insert the same ones again. PHP Solution 1 6-5 : Adding categories to the update form This PHP solution amends blog_update_mysqli .php from PHP Solution 1 5-2 in the previous chapter to. blog_update_mysqli .php from PHP Solution 1 5-2 . Alternatively, copy blog_update_mysqli_04 .php from the ch16 folder, and save it in the admin folder as blog_update_mysqli .php. 2. When the page

Ngày đăng: 06/07/2014, 19:20

Tài liệu cùng người dùng

Tài liệu liên quan