リスト項目のマーキングと削除
ここまでの段階で、PHP Webアプリケーションから軽量のSQLiteデータベースに対してSELECT、INSERT、UPDATEクエリを実行できるようになりました。あと必要なのは、DELETEコマンドでレコードを削除する機能と、UPDATEコマンドでレコードを完了済みとしてマークする機能です。
どちらの機能も、実装は特に難しくありません。項目を完了済みとしてマークするには、項目のstatusフィールドを1(保留)から0(完了)に変更します。この処理はdone.phpで行います。
<?php // check for record ID if (isset($_GET['id']) && is_numeric($_GET['id'])) { $id = (int) $_GET['id']; try { // connect to database $dbh = new PDO('sqlite:../../db/todo.db3'); // update item record // change status field $now = mktime(); $dbh->exec("UPDATE items SET status = '0', complete = '$now' WHERE id = '$id'"); // redirect to index page header('Location: index.php'); exit(); } catch (PDOException $e) { die('Error: ' . $e->getMessage()); } } else { die('Error in form submission'); } ?>
このスクリプトでは、UPDATEクエリを使用して、選択したレコードのstatusフィールドを0に変更します(クエリの実行にはやはりPDOのexec()メソッドを使用します)。また、現在の日付(完了日)をUNIXタイムスタンプ形式で保存していることに注意してください。
項目を削除するには、対象項目のレコードIDを使用してDELETEクエリを発行します。この処理はdelete.phpで行います。
<?php // check for record ID if (isset($_GET['id']) && is_numeric($_GET['id'])) { $id = (int) $_GET['id']; try { // connect to database $dbh = new PDO('sqlite:../../db/todo.db3'); // delete item $dbh->exec("DELETE FROM items WHERE id = '$id'"); // redirect to index page header('Location: index.php'); exit(); } catch (PDOException $e) { die('Error: ' . $e->getMessage()); } } else { die('Error in form submission'); } ?>
この時点で、今度はアプリケーションのインデックスページを修正し、各レコードの隣に新しい機能のリンクを追加する必要があります。修正版のインデックスページはリスト7のようになります。
この変更により、すべての保留項目に「Mark as Done」、「Change」、「Remove」というリンクが表示され、すべての完了項目に「Remove」リンクが表示されるようになります。さらに、修正版スクリプトではもう1つ機能を追加し、現在の日付(保留項目の場合)または完了日(完了項目の場合)を項目の実施期限と比較して、期限を過ぎている項目を赤で表示するようにしています。前掲の図1は、上記すべての機能を実装した状態のインデックスページです。
<?php require 'include.php'; try { // connect to database $dbh = new PDO('sqlite:../../db/todo.db3'); // query and retrieve pending items $sth = $dbh->query("SELECT * FROM items WHERE status = '1' ORDER BY due DESC, priority DESC"); $pending = $sth->fetchAll(); $sth = null; // query and retrieve completed items $sth = $dbh->query("SELECT * FROM items WHERE status = '0' ORDER BY due DESC, priority DESC"); $complete = $sth->fetchAll(); unset($dbh); } catch (PDOException $e) { die('Error: ' . $e->getMessage()); } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <title>To-Do List</title> <link rel="stylesheet" href="main.css" /> </head> <body> <h2>To-Do List</h2> <div class="category">Pending Items</div> <!-- generate listing of pending items --> <?php if (sizeof($pending) > 0) { ?> <table> <tr> <td class="head"></td> <td class="head">Due date</td> <td class="head">Priority</td> <td></td> <td></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="done.php?id= <?php echo (int) $p['id']; ?>">Mark as done</a> | </td> <td><a href="form.php?id= <?php echo (int) $p['id']; ?>">Change</a> | </td> <td><a href="delete.php?id= <?php echo (int) $p['id']; ?>">Remove</a></td> </tr> <?php } ?> </table> <?php } else { ?> <p>None</p> <?php } ?> <!-- link to add a new to-do item --> <p><a href="form.php">Add a new entry</a></p> <!-- generate listing of completed items --> <div class="category">Completed Items</div> <?php if (sizeof($complete) > 0) { ?> <table> <tr> <td class="head"></td> <td class="head">Due date</td> <td class="head">Completion date</td> <td></td> </tr> <?php foreach ($complete as $c) { $class = ($c['complete'] > $c['due']) ? 'late' : 'ontime'; ?> <tr> <td class="<?php echo $class; ?>"> <?php echo $c['name']; ?></td> <td class="<?php echo $class; ?>"> <?php echo date('d M Y', $c['due']); ?></td> <td class="<?php echo $class; ?>"> <?php echo date('d M Y', $c['complete']); ?></td> <td><a href="delete.php?id= <?php echo (int) $c['id']; ?>">Remove</a></td> </tr> <?php } ?> </table> <?php } else { ?> <p>None</p> <?php } ?> </body> </html>
データベースのバックアップと復元
これで作業リストアプリケーションは完成です。このアプリケーションを使用して作業項目を保存し、優先順位を指定または変更し、完了済みとしてマークし、完了した項目を削除することができます。しかし本稿を終える前に、このアプリケーションを運用するうえで重要な意味を持つ機能について触れておきたいと思います。その機能とはデータベースのバックアップです。
SQLiteデータベースをバックアップするのは簡単です。このデータベースは1つのディスクファイルに格納されているからです。最も簡単なバックアップ方法は、このディスクファイルをバックアップメディアにコピーして、安全な場所に保管することです。しかしその他に、SQLiteの組み込みの.dumpコマンドを使用するという方法もあります。このコマンドを実行すると、対象データベースを一から再生成するのに必要なCREATE TABLEコマンドとINSERTコマンドを含んだファイルを生成できます。.dumpコマンドを単独で実行した場合の結果は次のとおりです。
sqlite> .dump BEGIN TRANSACTION; CREATE TABLE items ( id integer not null primary key, name text not null, due integer not null, complete integer null, priority integer not null, status integer not null ); INSERT INTO "items" VALUES(1, 'Buy anniversary present',1184437800,NULL,4,1); INSERT INTO "items" VALUES(2, 'Send invoices',1180549800,NULL,5,1); INSERT INTO "items" VALUES(3, 'Finish homework',1180549800,NULL,1,0); INSERT INTO "items" VALUES(4, 'Visit Jane',1180290600,NULL,2,1); INSERT INTO "items" VALUES(5, 'Finalize hotel reservations',1181737000,NULL,5,1); INSERT INTO "items" VALUES(6, 'Angie''s birthday',1182537000,NULL,4,1); INSERT INTO "items" VALUES(7, 'Weed the lawn',1182237000,NULL,1,1); COMMIT;
このようにダンプ結果を画面に出力してもあまり意味がないので、ダンプ結果をファイルに出力するようにします。そのためには、.dumpコマンドの前に、.outputコマンドにファイル名を指定して実行します。
sqlite> .output todo.sql sqlite> .dump sqlite> .output stdout
このバックアップファイルからデータベースを復元することは簡単にできます。SQLiteの.readコマンドにバックアップファイルの名前を渡して実行すればよいのです。これにより、ファイル内のSQLが自動的に読み込まれて実行され、データベースが再作成されます。
sqlite> .read todo.sql
ここまで見てきたように、軽量データベースはWebアプリケーションで十分に利用できる段階まで来ており、各種のプラットフォーム、言語、機能セットをサポートする軽量データベースも登場しています。今度、データベース機能が必要だが大規模なデータベースを導入するほどのコストやリソースはないという状況に遭遇したら、ぜひ本稿で紹介したいずれかの軽量データベースを試してみてください。
