リスト項目の編集
リスト3およびリスト4で示したフォームとフォーム処理コードに少し手を加えれば、既存レコードの編集機能を実現できます。
しかしその前に、インデックスページを少し手直しして、各レコードの横に「Change」リンクを追加しましょう。そのためには、保留項目の表を作成するときに、「Change」リンクを含んだ<td>タグを新たに追加します。このリンクには、各項目のIDを含んだURLをリンク先として指定する必要があります。次のコードリストでは、この部分に関係するコードを太字で示しています。
...
<table>
<tr>
<td class="head"></td>
<td class="head">Due date</td>
<td class="head">Priority</td>
<td></td>
</tr>
<?php foreach ($pending as $p) {
$class = (mktime() > $p['due']) ? 'late' : 'ontime';
?>
<tr>
<td class="<?php echo $class; ?>">
<?php echo $p['name']; ?></td>
<td class="<?php echo $class; ?>">
<?php echo date('d M Y', $p['due']); ?></td>
<td class="<?php echo $class; ?>">
<?php echo $priorities[$p['priority']]; ?></td>
<td><a href="form.php?id=
<?php echo (int) $p['id']; ?>">Change</a></td>
</tr>
<?php } ?>
</table>
...
次に、form.phpを修正して、レコードIDを指定してこのスクリプトを呼び出したときに、対応するレコードがデータベースから自動的に取得され、フォームに埋め込まれるようにします。修正版のフォームはリスト5のようになります。
リスト5の第一の変更点は、スクリプトの最初の部分にさまざまなPDO呼び出しを追加していることです。このスクリプトは、レコードIDを指定して呼び出されると、新しいPDO接続を初期化し、SELECTクエリを使用して、該当レコードをフィールド-値のペアの配列という形で取得します。この部分では、dueフィールドのUNIXタイムスタンプ値を日、月、年の数値に分けるという処理も行っています。
さらに、フォーム部分のさまざまな入力フィールドのコードにも手を加え、query()およびfetch()で生成したレコードセット配列から適切な値を取得してフォームに埋め込むようにしています。入力フィールドがドロップダウンリストの場合は、条件テストを使用して、あらかじめ適切なオプションが自動的に選択されるようにします。またこの修正版スクリプトでは、取得するレコードのIDを格納するためのidという隠しフィールドを追加しています(このフィールドはリスト6で使われます)。
<?php require 'include.php'; // get current year $year = date('Y'); $record = null; if (isset($_GET['id']) && is_numeric($_GET['id'])) { $id = (int) $_GET['id']; try { // connect to database $dbh = new PDO('sqlite:../../db/todo.db3'); // retrieve item record $sth = $dbh->query("SELECT * FROM items WHERE id = '$id'"); $record = $sth->fetch(); // break due date timestamp into constituent parts $date = getdate($record['due']); $record['due_mm'] = $date['mon']; $record['due_yy'] = $date['year']; $record['due_dd'] = $date['mday']; unset($dbh); } catch (PDOException $e) { die('Error: ' . $e->getMessage()); } } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <title>To-Do Form</title> <link rel="stylesheet" href="main.css" /> </head> <body> <h2>Enter To-Do Item</h2> <form method="post" action="save.php"> <table> <tr> <td><label>Title:</label></td> <td><input type="text" name="name" class="txt" value=" <?php echo $record['name']; ?>"></td> </tr> <tr> <td><label>Due Date:</label></td> <td> <select name="due_dd" class="txt"> <?php for ($x=1; $x<=31; $x++) { $selected = ($record['due_dd'] == $x) ? 'selected' : ''; ?> <option value="<?php echo $x; ?>" <?php echo $selected; ?>><?php echo $x; ?></option> <?php } ?> </select> <select name="due_mm" class="txt"> <?php for ($x=1; $x<=12; $x++) { $selected = ($record['due_mm'] == $x) ? 'selected' : ''; ?> <option value="<?php echo $x; ?>" <?php echo $selected; ?>><?php echo $months[$x]; ?> </option> <?php } ?> </select> <select name="due_yy" class="txt"> <?php for ($x=$year; $x<=($year+5); $x++) { $selected = ($record['due_yy'] == $x) ? 'selected' : ''; ?> <option value="<?php echo $x; ?>" <?php echo $selected; ?>><?php echo $x; ?></option> <?php } ?> </select> </td> </tr> <tr> <td><label>Priority:</label></td> <td> <select name="priority" class="txt"> <?php for ($x=1; $x<=5; $x++) { $selected = ($record['priority'] == $x) ? 'selected' : ''; ?> <option value="<?php echo $x; ?>" <?php echo $selected; ?>> <?php echo $priorities[$x]; ?></option> <?php } ?> </select> </td> </tr> </table> <input type="hidden" name="id" value="<?php echo $record['id']; ?>"> <p> <input type="submit" name="submit" value="Submit" class="btn"> </p> </form> </body> </html>
form.phpに上記の変更を加えたことにより、save.phpスクリプトにも変更の必要性が出てきます。ユーザーからフォームが送信されたときに、送信されたデータが既存レコードのものなのか新規レコードなのかを判断し、状況に応じて適切なUPDATEクエリまたはINSERTクエリを生成しなければならないのです。そこで、save.phpにも変更を加えます。修正版のsave.phpはリスト6のようになります。
リスト6の変更点はそれほど多くありません。修正版スクリプトでは、idという隠しフィールドをフラグとして利用し、UPDATEクエリとINSERTクエリのどちらを生成するかを判断しています。PDOのexec()メソッドを前述のように使用して、データベースに対してクエリを実行します。
<?php if (isset($_POST['submit'])) { // check if record ID exists in the form submission if (isset($_POST['id']) && is_numeric(trim($_POST['id']))) { $id = (int) $_POST['id']; } // perform input validation // add errors to $errors array $errors = array(); // check name $name = stripslashes($_POST['name']); if (!isset($_POST['name']) || !preg_match( "/^[a-z0-9][a-z0-9\\'\\.[:space:]]+$/i", $name)) { $errors[] = 'Invalid title'; } // check priority if (!isset($_POST['priority']) || !is_numeric($_POST['priority'])) { $errors[] = 'Invalid priority'; } // check date validity if (!checkdate($_POST['due_mm'], $_POST['due_dd'], $_POST['due_yy'])) { $errors[] = 'Invalid date'; } // print errors and exit // if no errors, proceed to save the record to the database if (sizeof($errors) > 0) { die('Error: <br/>' . implode($errors, '<br/>')); } else { try { // connect to database $dbh = new PDO('sqlite:../../db/todo.db3'); $dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); // quote all input characters $name = $dbh->quote($name); $priority = (int) $_POST['priority']; // generate UNIX timestamp for due date $due = $dbh->quote(mktime( 0,0,0,$_POST['due_mm'],$_POST['due_dd'], $_POST['due_yy'])); // generate UPDATE or INSERT query if (isset($id)) { $sql = "UPDATE items SET name=$name, due=$due, priority='$priority',status='1' WHERE id='$id'"; } else { $sql = "INSERT INTO items ( name, due, priority, status) VALUES ( $name, $due, '$priority', '1')"; } // execute query $dbh->exec($sql); unset($dbh); // redirect to index page header('Location: index.php'); exit(); } catch (PDOException $e) { die('Error: ' . $e->getMessage()); } } } else { die('Error in form submission'); } ?>
