1. Trang chủ
  2. » Công Nghệ Thông Tin

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

10 281 0
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 564,1 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Preserving referential integrity on deletion In PHP Solution 16-5, there was no need to worry about referential integrity when you deleted records in the cross-reference table because t

Trang 1

} else {

$sql = 'UPDATE blog SET image_id = NULL, title = ?, article = ?

WHERE article_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('ssi', $_POST['title'], $_POST['article'], 

$_POST['article_id']);

}

$stmt->execute();

$done = $stmt->affected_rows;

}

The last two lines of this code block execute the prepared statement that updates the record in the blog table, and then assign the number of affected rows to $done If you update a record, the affected_rows property is 1, which is treated as true However, if you dont make any changes to the record, affected_rows is 0, which is treated as false If you update only the categories associated with a record, without changing the record itself, $done is interpreted as false, and you wont be returned to blog_list_mysqli.php

Delete the following line:

$done = $stmt->affected_rows;

6 Assign the return value of $stmt->execute() to $done like this:

$done = $stmt->execute();

The execute() method returns true if the prepared statement is executed successfully, even if

it doesnt result in any changes to the record

7 Immediately after the line you have just edited, insert the code to delete existing values in the

cross reference table and to insert the newly selected values like this:

$done = $stmt->execute();

// delete existing values in the cross-reference table

$sql = 'DELETE FROM article2cat WHERE article_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['article_id']);

$stmt->execute();

// insert the new values in articles2cat

if (isset($_POST['category']) && is_numeric($_POST['article_id'])) {

$article_id = (int) $_POST['article_id'];

foreach ($_POST['category'] as $cat_id) {

$values[] = "($article_id, " (int) $cat_id ')';

}

if ($values) {

$sql = 'INSERT INTO article2cat (article_id, cat_id)

Trang 2

452

}

}

}

}

This code needs little explanation The DELETE query removes all entries in the cross-reference table that match article_id The remaining code inserts the values selected in the update form Its identical to the code in step 4 of PHP Solution 16-4 The key thing to note is that it uses

an INSERT query, not UPDATE The original values have been deleted, so youre adding them anew

8. Save blog_update_mysqli.php, and test it by updating existing records in the blog table You can check your code, if necessary, against blog_update_mysqli_06.php in the ch16 folder The PDO version is in blog_update_pdo_06.php

Preserving referential integrity on deletion

In PHP Solution 16-5, there was no need to worry about referential integrity when you deleted records in the cross-reference table because the values stored in each record are foreign keys Each record simply refers

to the primary keys stored in the blog and categories tables Referring to Figure 16-1 at the beginning of this chapter, deleting from the cross-reference table the record that combines article_id 2 with cat_id 1 simply breaks the link between the article titled “Trainee Geishas Go Shopping” and the Kyoto category Neither the article nor the category is affected They both remain in their respective tables

The situation is very different if you decide to delete either the article or the category If you delete the

“Trainee Geishas Go Shopping” article from the blog table, all references to article_id 2 must also be deleted from the cross-reference table Similarly, if you delete the Kyoto category, all references to cat_id

1 must be removed from the cross-reference table Alternatively, you must halt the deletion if an items primary key is stored elsewhere as a foreign key

The best way to do this is through the establishment of foreign key restraints To do so, you need to convert the storage engine of related tables to InnoDB

PHP Solution 16-6: Converting tables to the InnoDB storage engine

This PHP solution shows how to use phpMyAdmin to convert the storage engine of database tables from MyISAM to InnoDB If you plan to upload the tables to your remote server, it must also support InnoDB (see PHP Solution 16-1)

1. Select the phpsols database in phpMyAdmin, and then select the article2cat table

2 Click the Operations tab at the top right of the screen

3 In the Table options section, select InnoDB from the Storage Engine drop-down menu, as

shown in Figure 16-10

Trang 3

Figure 16-10 Changing a tables storage engine is very easy in phpMyAdmin

4 Click Go Changing the storage engine is as simple as that!

5 All tables related to each other through foreign key relationships need to use InnoDB Repeat

steps 1–4 with the blog, categories, and images tables

PHP Solution 16-7: Setting up foreign key constraints

This PHP solution describes how to set up foreign key constraints between the article2cat, blog, and category tables in phpMyAdmin The foreign key constraints must always be defined in the child table In this this case, the child table is article2cat, because it stores the article_id and cat_id primary keys from the other tables as foreign keys

1 Select the article2cat table in phpMyAdmin, and click the Structure tab

2 Click Relation view (circled in Figure 16-11) at the bottom of the structure table

Figure 16-11 Foreign key constraints are defined in phpMyAdmins Relation view

3 Foreign key constraints can be set up only on columns that are indexed The article_id and

cat_id columns in article2cat are the tables composite primary key, so theyre both listed in

the screen that opens If your version of phpMyAdmin has an option labeled Internal relations, you can ignore it The section youre interested in is labeled FOREIGN KEY (INNODB)

Trang 4

454

Figure 16-12 This will be used to establish a formal foreign key relationship between article_id

in the article2cat table and article_id in the blog table

Figure 16-12 Selecting the primary key in the parent table

The ON DELETE drop-down menus have the following options:

 CASCADE: When you delete a record in the parent table, all dependent records are

deleted in the child table For example, if you delete the record with the primary key article_id 2 in the blog table, all records with article_id 2 in the article2cat table are automatically deleted

 SET NULL: When you delete a record in the parent table, all dependent records in

the child table have the foreign key set to NULL The foreign key column must accept NULL values

 NO ACTION: On some database systems, this allows foreign constraint checks to

be delayed MySQL performs checks immediately, so this has the same effect as

RESTRICT

 RESTRICT: This prevents the deletion of a record in the parent table if dependent

records still exist in the child table

The same options are available for ON UPDATE With the exception of RESTRICT, they are of limited interest, because you should change the primary key of a record only in exceptional circumstances ON

UPDATE RESTRICT not only stops changes being made to the primary key in the parent table; it also

rejects any inserts or updates in the child table that would result in foreign key values that dont have a match in the parent table

In the case of a cross-reference table, CASCADE is the logical choice If you decide to delete a

record in the parent table, you want all cross-references to that record to be removed at the same time However, to demonstrate the default behavior of foreign key constraints, select

RESTRICT Leave ON UPDATE blank

4 In the cat_id row, select `phpsols`.`categories`.`cat_id` from the drop-down menu

immediate to the left of ON DELETE, and set ON DELETE to RESTRICT Click Save

If RESTRICT isnt available in the drop-down menu, leave the option blank

5 If you have not already done so, update at least one blog entry to associate it with a category

Trang 5

6 In phpMyAdmin, select the categories table, and click the Dele te icon next to a category that

you know to be associated with a blog entry, as shown in Figure 16-13

Figure 16-13 Click the large red X to delete a record in phpMyAdmin

7 Click OK when phpMyAdmin asks you to confirm the deletion If you have set up the foreign key

constraints correctly, youll see the error message shown in Figure 16-14

Figure 16-14 The foreign key constraint prevents the deletion if dependent records exist

8 Select the article2cat table, and click the Structure tab Then click Relation view In all

probability, the ON DELETE options will be blank This is not a cause for concern, RESTRICT is

the default for both ON DELETE and ON UPDATE Leaving these options blank has the same effect as selecting RESTRICT

9 Change both ON DELETE settings to CASCADE, and click Save

10 Select a record in the blog table that you know is associated with a category, and delete it

11 Check the article2cat table The records associated with the record you have just deleted

have also been deleted

To continue your exploration of foreign key constraints, select the blog table, and establish a foreign key relationship with image_id in the images table If you delete a record from the images table, the image_id

foreign key in the blog table needs to be set to NULL This is done automatically if you set the value of ON

DELETE to SET NULL Test it by deleting a record from the images table and checking the associated

record(s) in the blog table

If you need to convert an InnoDB table back to MyISAM, you must first remove any foreign key

constraints Select Relation view, set all fields to blank, and click Save After removing the foreign key

Trang 6

456

Creating delete scripts with foreign key constraints

Choosing the values for ON DELETE in InnoDB tables depends on the nature of the relationship between tables In the case of the phpsols database, its not only safe but desirable to set the option to CASCADE

for both columns in the article2cat cross-reference table If a record is deleted in either the blog or categories parent table, the related values need to be deleted in the cross-reference table

The relationship between the images and blog tables is different If you delete a record from the images

table, you probably dont want to delete related articles in the blog table In that case, SET NULL is an

appropriate choice When a record is deleted from the images table, the foreign key in related articles is set

to NULL, but the articles remain intact

On the other hand, if images are vital to the understanding of articles, select RESTRICT Any attempt to

delete an image that still has related articles is automatically halted

These considerations affect how you handle deletion scripts When the foreign key constraint is set to

CASCADE or SET NULL, you dont need to do anything special You can use a simple DELETE query and

leave the rest to MySQL

However, if the foreign key constraint is set to RESTRICT, the DELETE query will fail To display an appropriate error message, use the errno property of a MySQLi statement object The MySQL error code for

a query that fails as a result of a foreign key constraint is 1451 After calling the execute() method, you can check for errors like this in MySQLi:

$stmt->execute();

if ($stmt->affected_rows > 0) {

$deleted = true;

} else {

$deleted = false;

if ($stmt->errno == 1451) {

$error = 'That record has dependent files in a child table, and cannot be 

deleted.';

} else {

$error = 'There was a problem deleting the record.';

}

}

If you are using PDO, use the errorCode() method The code for a query that fails as a result of a foreign key constraint is HY000 After checking the number of affected rows with rowCount(), you can check the error code like this with PDO:

$deleted = $stmt->rowCount();

if (!$deleted) {

if ($stmt->errorCode() == 'HY000') {

$error = 'That record has dependent files in a child table, and cannot be 

deleted.';

} else {

$error = 'There was a problem deleting the record.';

}

}

Trang 7

The error codes in the PDO and MySQLi versions are different because PDO uses the codes defined by the ANSI SQL standard, whereas MySQLi uses MySQL-specific codes

Creating delete scripts without foreign key constraints

If you cant use InnoDB tables, you need to build the same logic into your own delete scripts To achieve the

same effect as ON DELETE CASCADE, run two consecutive DELETE queries like this:

$sql = 'DELETE FROM article2cat WHERE article_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['article_id']);

$stmt->execute();

$sql = 'DELETE FROM blog WHERE article_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['article_id']);

$stmt->execute();

To achieve the same effect as ON DELETE SET NULL, run an UPDATE query combined with a DELETE query

like this:

$sql = 'UPDATE blog SET image_id = NULL WHERE image_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['image_id']);

$stmt->execute();

$sql = 'DELETE FROM images WHERE image_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['image_id']);

$stmt->execute();

To achieve the same effect as ON DELETE RESTRICT, you need to run a SELECT query to find if there are

dependent records before continuing with the DELETE query like this:

$sql = 'SELECT image_id FROM blog WHERE image_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['image_id']);

$stmt->execute();

// if num_rows is not 0, there are dependent records

if ($stmt->num_rows) {

$error = 'That record has dependent files in a child table, and cannot be deleted.'; } else {

$sql = 'DELETE FROM images WHERE image_id = ?';

$stmt->prepare($sql);

$stmt->bind_param('i', $_POST['image_id']);

$stmt->execute();

}

Trang 8

458

Chapter review

Once you have learned basic SQL and the PHP commands to communicate with a database, working with single tables is very easy Linking tables through foreign keys, however, can be quite challenging The power of a relational database comes from its sheer flexibility The problem is that this infinite flexibility means there is no single “right” way of doing things

Dont let this put you off, though Your instinct may be to stick with single tables, but down that route lies even greater complexity The key to making it easy to work with databases is to limit your ambitions in the early stages Build simple structures like the one in this chapter, experiment with them, and get to know how they work Add tables and foreign key links gradually People with a lot of experience working with databases say they frequently spend more than half the development time just thinking about the table structure After that, the coding is the easy bit!

In the final chapter, we move back to working with a single table—addressing the important subject of user authentication with a database and how to handle encrypted passwords

Trang 9

Authenticating Users with a Database

Chapter 9 showed you the principles of user authentication and sessions to password protect parts of your website, but the login scripts all relied on usernames and passwords stored in text files Keeping user details in a database is both more secure and more efficient Instead of just storing a list of usernames and passwords, a database can store other details, such as first name, family name, email address, and

so on MySQL also gives you the option of using either one- or two-way encryption In the first section of this chapter, well examine the difference between the two Then youll create registration and login scripts for both types of encryption

What this chapter contains:

• Deciding how to encrypt passwords

• Using one-way encryption for user registration and login

• Using two-way encryption for user registration and login

• Decrypting passwords

Choosing an encryption method

The PHP solutions in Chapter 9 use the SHA-1 encryption algorithm It offers a high level of security,

particularly if used in conjunction with a salt (a random value thats added to make decryption harder)

SHA-1 is a one-way encryption method: once a password has been encrypted, theres no way of converting it back to plain text This is both an advantage and a disadvantage It offers the user greater security because passwords encrypted this way remain secret However, theres no way of reissuing a lost password, since not even the site administrator can decrypt it The only solution is to issue the user a temporary new password, and ask the user to reset it

The alternative is to use two-way encryption, which relies on a pair of functions: one to encrypt the

Trang 10

460

the key in your registration and login scripts—either directly or through an include file—so if your scripts are ever exposed, your security is blown wide apart MySQL offers a number of two-way encryption functions, but AES_ENCRYPT() is considered the most secure It uses the Advanced Encryption Standard with a 128-bit key length (AES-128) approved by the U.S government for the protection of classified material up to the SECRET level (TOP SECRET material requires AES-192 or AES-256)

Both one-way and two-way encryption have advantages and disadvantages Many security experts recommend that passwords should be changed frequently So, forcing a user to change a forgotten password because it cant be decrypted could be regarded as a good security measure On the other hand, users are likely to be frustrated by the need to deal with a new password each time they forget the existing one Ill leave it to you to decide which approach is best suited to your circumstances, and Ill concentrate solely on the technical implementation

Using one-way encryption

In the interests of keeping things simple, Im going to use the same basic forms as in Chapter 9, so only the username, salt, and encrypted password are stored in the database

Creating a table to store users details

In phpMyAdmin, create a new table called users in the phpsols database The table needs four columns (fields) with the settings listed in Table 17-1

Table 17-1 Settings for the users table

Field Type Length/Values Attributes Null Index A_I

To ensure no one can register the same username as one thats already in use, the username column is given an UNIQUE index

In Chapter 9, the username doubled as the salt, but storing the details in a database means that you can choose something more unique and difficult to guess Although a Unix timestamp follows a predictable pattern, it changes every second So even if an attacker knows the day on which a user registered, there are 86,400 possible values for the salt, which would need to be combined with every attempt to guess the password So the salt column needs to store an integer (INT)

The pwd column, which is where the encrypted password is stored, needs to be 40 characters long because the SHA-1 algorithm always produces an alphanumeric string of that length Its a fixed length, so CHAR is used in preference to VARCHAR The CHAR data type is more efficient when dealing with fixed-length strings

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

TỪ KHÓA LIÊN QUAN

w