ここでは,登録画面や検索画面などのデータベースを用いた具体的なWebアプリ ケーションの作成方法について説明する.
登録画面は,入力フォームを表示する画面 (touroku.html )と,フォー ムの内容を受け取って実際にデータベースにデータを登録するスクリプト (touroku.cgi)(touroku.plでも可)から成る.登録処理のイ メージは次のようになる.
はじめに,データを登録するテーブルを作成する.今回作成するテーブルは次の ような単純な構造とする.(今回の基本がわかれば,いくらでも複雑なデータベ ースを設計することができる.)
| フィールド名 | 型 | 説明 |
| id | int(オートインクリメント型) | ID番号 |
| name | varchar(29) | 名前 |
| age | int | 年齢 |
テーブル名はtsumiki_tblとして,主キーをidとする.idはオートインクリメント 型で自動的に連番が振られるようにする. このテーブルをSQL文で作成する.
create table tsumiki_tbl( id int not null auto_increment, name varchar(20) not null, age int not null, primary key (id) )
データの入力画面(touroku.html) を作成する.idは自動的にデータベースで連番を割振らせるため,入力項目は名 前(name)と年齢(age)のみになる.以下は,フォームを用いたデータの入力画面 である.続いて,HTMLのソースを示す.
<html> <body> <form action="cgi-bin/touroku.cgi" method="post"> 名前:<input type="text" name="nm"> 年齢:<input type="text" name="age"> <input type="submit" name="exec" value="登録"> </form> </body> </html>
名前と年齢を入力した後,登録ボタンを押すと,”POST”によってデータ送信され, touroku.cgiのcgiプログラム(perlプログラム) が動作するようになっている.
フォームから送られた内容をデータベースに登録するスクリプト (touroku.cgi)を作成する.
#! C:/ActivePerl/bin/perl
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# INSERT文作成
$sql="insert into tsumiki_tbl(name,age) ";
$sql.="values('" . $form->param('nm') . "'," . $form->param('age') . ")";
# SQL実行
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "登録完了\n";
print "</body></html>\n";
登録スクリプトは,フォームから送信されてきた変数nm, ageをもとにINSERT文を作成し,データベース に登録している.idはオートインクリメント型で自動的に割り振られるため,INSERT文では省略している.


登録データの一覧を表示するには,データベースからレコードを読み込んで出力 する.tsumiki_tblの内容を全て出力するには次のような登録データ一覧表示 (tblshow.html)とそのスクリプト (tblshow.cgi)を作成する.
<html> <body> <form action="cgi-bin/tblshow.cgi" method="post"> <input type="submit" name="exec" value="データベースの表示"> </form> </body> </html>
tblshow.cgiのスクリプトを以下に示す.
#! C:/ActivePerl/bin/perl
use DBI;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# SQL実行
$sth = $db->prepare("select id,name,age from tsumiki_tbl");
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# 結果出力
print "<table border=1>\n";
print "<tr><td>id</td><td>name</td><td>age</td></tr>\n";
while (@rec = $sth->fetchrow_array) {
print "<tr>\n";
print "<td>" . $rec[0] . "</td>\n";
print "<td>" . $rec[1] . "</td>\n";
print "<td>" . $rec[2] . "</td>\n";
print "</tr>\n";
}
print "</table>\n";
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
この例では,SELECT文で全レコードを取得し,その後HTMLのテーブルを用いて検索結果を出力して いる.fetchrow_arrayで取得した配列から個々のフィールドの値を取り出すには, $rec[フィールド番号]とする.
検索画面は,検索条件を入力する画面 (kensaku.html)と,入力された検索条件を受け取って検索を 行うスクリプト(kensaku.cgi)から成る. 検索対象のテーブルはtsumiki_tblで,名前でデータを 検索するものとする.検索処理の全体像を以下の図に示す.
検索条件の入力画面(kensaku.html) を作成する.検索は名前で行う.
<html> <body> <form action="cgi-bin/kensaku.cgi" method="post"> 名前:<input type="text" name="nm"> <input type="submit" name="exec" value="検索"> </form> </body> </html>
#! C:\ActivePerl\bin\perl
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","knk3503t",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# SELECT文作成
$sql = "select id,name,age from tsumiki_tbl";
$sql .= "where name like '%" . $form->param('nm') . "%'";
# SQL実行
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# 結果出力
print "<table border=1>\n";
print "<tr><td>id</td><td>name</td><td>age</td></tr>\n";
while (@rec = $sth->fetchrow_array) {
print "<tr>\n";
print "<td>" . $rec[0] . "</td>\n";
print "<td>" . $rec[1] . "</td>\n";
print "<td>" . $rec[2] . "</td>\n";
print "</tr>\n";
}
print "</table>\n";
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
検索条件のLIKEでは,入力された条件param('nm')の前後に%(ワイルドカード)を付けた.この結果, 名前の中にparam('nm')が含まれるデータが全て取得される.

インターネットの検索エンジンなどでは,よく「次のlO件」などのようにぺ一ジ を分割している.ここでは,検索結果の分割を考える.この機能での重要項目は, SELECT文のLIMITオプションである.LIMITは取得するレコードの取得開始位置と 取得数を指定するオプションで,この機能を有効に使うことにより,検索結果の ページを分割する.
検索対象のテーブルは,引き続きtsumik_tblを使う.テーブルの検索結果を10件 ごとに分割して表示する.用意するファイルは,検索条作入力フォーム (bunkatsu.html)と検索処理用スクリプト (bunkatsu.cgi)である.
ぺージの分割処理は,現在のページ番号を引き渡すことによって実現する.まず 検索条件入力画面から検索条件とページ番号=0を引き渡す.検索処理スクリプ トは送られてきた条件をもとに検索を行う.ただし,このスクリプトでは,ぺ一 ジ番号によって取得するレコードの位置を変える仕掛けをしておく.以下に,検 索処理の概念図を示す.
検索結果の画面には,「前の10件」「次の10件」というリンクを貼り付けて おく.このリンクは,検索処理用スクリプトへのリンクでURLのクエリー文字列 を使って,ぺ一ジ番号と検索条件を送るようにする.「前の10件」は現在のペ ージ番号−1を,「次の10件」の場合は現在のページ番号+1を送るようにし ておけば,次に検索処理用スクリプトが呼ばれた時は,ぺ一ジが入れ替わって表 示されるようになる。
初めに,検索条件の入力画面(bunkatsu.html)を作成する. 検索条件は名前のみで,名前のテキスト入力項目を作成する.注目すべきはhiddenで ページ数を設定して,そのページ数の設定を0にしていることである.
<html> <body> <form action="cgi-bin/bunkatsu.cgi" method="post"> 名前:<input type="text" name="nm"> <input type="hidden" name="page_num" value="0"> <input type="submit" name="exec" value="検索"> </form> </body> </html>
検索処理スクリプト(bunkatsu.cgi)は次のようになる.
#!C:/ActivePerl/bin/perl
use POSIX;
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# LIMITを使ったSELECT文を作成
$sql = "select id,name,age from tsumiki_tbl ";
$sql .= "where name like '%" . $form->param('nm') . "%' order by id ";
$sql .= "limit ". $form->param('page_num')*10 ." ,10";
# SQL実行
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# 結果出力
print "<table border=1>\n";
print "<tr><td>id</td><td>name</td><td>age</td></tr>\n";
while (@rec = $sth->fetchrow_array) {
print "<tr>\n";
print "<td>" . $rec[0] . "</td>\n";
print "<td>" . $rec[1] . "</td>\n";
print "<td>" . $rec[2] . "</td>\n";
print "</tr>\n";
}
print "</table>\n";
# 検索条件に該当する全データの件数取得
$sql = "select count(*) from tsumiki_tbl ";
$sql .= "where name like '%" . $form->param('nm') . "%' order by id ";
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
@rec = $sth->fetchrow_array;
$cnt = $rec[0];
# ページ数表示
if($cnt>10){
print ceil($cnt/10) , "ページ中の\n";
print $form->param('page_num')+1 , "ページ目を表示<br>\n";
}
# 前の10件
if($form->param('page_num')!=0){
print "<a href=bunkatsu.cgi?nm=". $form->param('nm');
print "&page_num=" , $form->param('page_num')-1 , ">" , "\n";
print "<前の10件</a> \n";
}
# 次の10件
if(($form->param('page_num')+1)*10 < $cnt){
print "<a href=bunkatsu.cgi?nm=" , $form->param('nm');
print "&page_num=" , $form->param('page_num')+1 , ">\n";
print "次の10件></a>\n";
}
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
検索処理スクリプトでは,まず始めにSELECT文を実行する.フォームから送信された 変数nmをもとにWHERE句を作成する.重要なのはその後のLimit である.$page_num*10というLimitのオフセット位置を設定している.検索条件入力 画面からこのスクリプトが実行された場合,page_numは0が設定されているため,0件目,つまり 先頭からレコードが取得される.このpage_numを1に設定すると10件目からレコードの取得 が行われる.このSQL文によって,具体的なレコードの分割が行われる.
SELECT文実行後は,取得したレコードをテーブルで表示する.
次に,検索条件に合致するレコードの総数を取得する.これは次の「総ページ数」の判定や 「前の10件」の算出の際に必要となる.
ページ数は,レコード件数($cnt)が11件以上,つまり2ページ分ある場合にのみ 出力する.総ページはレコード件数÷10で算出している.ceilは小数点切り上げ の関数である.
「前の10件」は,現在のページ数が0以外の場合に出力する.前の10件はこのスクリプト に対してページ番号−1のリンクを貼ることで実現する. bunkatsu.cgiの後の ?は「この後にフォーム変数を記述する」と言う意味で,ここに検索条件$nm と現在のページ番号を-1した$page_numを指定している.
最後に「次の10件」を出力する.次の10件はまだ表示すべきレコードが存在する場合のみ 出力する.「前の10件」と同様このスクリプトに対してリンクを貼ることで実現している. 次の10件の場合は,$page_numを+1する.
まず,登録されているデータの一覧を表示し,その後にボタンで変更・削除対象のデータを処理する方法を 紹介する.
操作対象のテーブルを引き続きtsumiki_tblとする. このテーブルに登録されているデータの一覧を表示するスクリプトを ichiran.cgi とする.このスクリプトでは,レコード毎に「更新ボタン」と「削除ボタン」が付加されている. ボタンをフォームで作成するが,このフォームにはHIDDEN項目として「主キー」の 要素を含めておく.
更新・削除スクリプトでは,送られてきたHIDDEN項目の「主キー」によってSQLのWHERE条件を 作成する.「更新ボタン」が押されたときに呼び出されるスクリプトを koushin_input.cgiとする. このスクリプトによって,更新データ入力画面を表示する. 更新データ入力画面には現在登録されているデータが表示されるようにする.
データの編集後は,koushin.cgiを呼び出し, 実際にデータベースへの更新を行う. データ一覧から「削除ボタン」が押された場合に呼び出されるスクリプトを sakujo.cgiとする. このスクリプトではレコードを削除する.更新・削除処理のイメージを以下に示す.
まず,登録されているデータ一覧出力を行う画面を作成する.出力する項目は, 「名前」と「年齢」で,「ID」はHIDDEN項目としてボタンのHIDDEN項目に設定する. スクリプトichiran.cgiは以下のようになる.
#!C:/ActivePerl/bin/perl
use DBI;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# SQL実行
$sth = $db->prepare("select id,name,age from tsumiki_tbl");
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# 結果出力
print "<table border=1>\n";
print "<tr><td>name</td><td>age</td></tr>\n";
while (@rec = $sth->fetchrow_array) {
print "<tr>\n";
print "<td>" . $rec[1] . "</td>\n";
print "<td>" . $rec[2] . "</td>\n";
print "<form action=koushin_input.cgi method=post>\n";
print "<input type=hidden name=id value=" . $rec[0] . ">\n";
print "<td><input type=submit value=更新></td>\n";
print "</form>";
print "<form action=sakujo.cgi method=post>\n";
print "<input type=hidden name=id value=" . $rec[0] . ">\n";
print "<td><input type=submit value=削除></td>\n";
print "</form>\n";
print "</tr>\n";
}
print "</table>\n";
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
実際にデータベースの内容を更新するスクリプトを作成する前に, ユーザに更新する内容を入力してもらう画面を作成する必要がある. 更新対象のレコードの主キーは一覧画面から変数idによって送信されるので, まず始めにそのidをもとに現在の内容を出力する.後は,実際の更新を行う更新ボタンを作成する だけである.更新処理を行う場合も主キーが必要となるので,HIDDENタグで送信する. スクリプト(koushin_input.cgi)は以下のようになる.
#!C:/ActivePerl/bin/perl
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# SQL実行
$sql = "select name,age from tsumiki_tbl where id = '". $form->param('id'). "'";
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
@rec = $sth->fetchrow_array;
if(!@rec){
print "データが削除されています\n";
exit;
}
# 入力画面の出力
print "<form action=koushin.cgi method=post>\n";
print "名前:<input type=text name=nm value=\"" . $rec[0] . "\"> \n";
print "年齢:<input type=text name=age value=" . $rec[1] . "> \n";
print "<input type=hidden name=id value=" . $form->param('id') . ">\n";
print "<input type=submit value=更新>\n";
print "</form>\n";
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
このスクリプトでは,まず始めに送信されきたIDをもとに更新対象のレコードを検索する. この際,対象のデータが既に削除されていることを考慮して,データが存在しているかを チェックしている.無事にデータの取得ができた場合には,取得したデータをフォームとして 出力する.フォームからはデータベースの更新を行うスクリプト (koushin.cgi)を呼び出す.
更新データ入力画面(koushin_input.cgi)からは, name, age, id の3つの変数が送信されてくる.このスクリプト (koushin.cgi)では,この変数をもとにSQL文 のUPDATE文を作成し,レコードの更新を行う.
#!C:/ActivePerl/bin/perl
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# UPDATE文作成
$sql = "update tsumiki_tbl set ";
$sql .= "name='" . $form->param('nm') . "',";
$sql .= "age='" . $form->param('age') . "' ";
$sql .= "where id =" . $form->param('id');
# SQL実行
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "更新完了\n";
print "</body></html>\n";
データ一覧画面(ichiran.cgi)からは デーブルの主キーであるIDが変数idとして送信される.削除スクリプト (sakujo.cgi)では,このidをもとに,SQL文の DELETE文を作成し,レコードの削除を行う.作成するスクリプト (sakjo.cgi)は以下のようになる.
#!C:ActivePerl/bin/perl
use CGI;
use DBI;
$form = CGI->new;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# SQL実行
$sql = "delete from tsumiki_tbl where id=" . $form->param('id');
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "削除完了\n";
print "</body></html>\n";
削除更新の一連の確認を行う.
ここでは,登録されたデータをもとに簡単な棒グラフを作成する方法を紹介する.棒グラフの材料は tsumiki_tblのageで,どの年齢にどれだけの人数がいるかを表示したい.
グラフの出力は,あらかじめ用意したPNG画像を引き延ばす形式で行う.用意する画像は10×10 ピクセルの大きさで,この画像の幅をIMAGEタグで調整することによって棒グラフを表現する. また,年齢の集計はSELECT文のGROUP BY句を使って行う.グラフ出力のスクリプト (graph.cgi) で使用する画像の名前をblock.pngとする. 以下にグラフ処理のイメージ示す.
グラフの出力は,まずもととなるデータを取得することから始める.年齢毎にどれだけの人数がいるかを 確認するには,SELECT文でGROUP BY句を使う.今回は年齢毎なので,age でグルーピングをかける.取得する項目は年齢とその人数なので,ageとcount(*) を指定する.対象データが取得できれば後はグラフを出力するだけであり,スクリプト (graph.cgi)は以下のようになる.
#!C:/ActivePerl/bin/perl
use DBI;
print "Content-type: text/html\n\n";
print "<html><body>\n";
# DBに接続
$db=DBI->connect("DBI:mysql:tsumiki","togashi","passwd",
{RaiseError => 0, PrintError => 1});
if(!$db){
print "接続失敗\n";
exit;
}
# レコード件数の取得
$sql="select count(*) from tsumiki_tbl";
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
@rec = $sth->fetchrow_array;
$all_num=$rec[0];
# 年齢毎の集計
$sql="select age,count(*) as cnt from tsumiki_tbl ";
$sql.="group by age order by cnt desc,age";
$sth = $db->prepare($sql);
if(!$sth->execute){
print "SQL失敗\n";
exit;
}
# 検索した結果を全部表示
print "<table border=1>\n";
print "<tr><td>年齢</td><td>グラフ</td><td>人数</td><td>\n";
while (@rec = $sth->fetchrow_array) {
$percent=$rec[1]/$all_num*100;
print "<tr>\n";
print "<td>". $rec[0] . "</td>\n";
print "<td>\n";
print "<img src=../block.png height=10 width=";
print $percent*2;
print "> \n";
print int($percent*10+0.5)/10,"%\n";
print "</td>\n";
print "<td>" . $rec[1] . "</td>\n";
print "</tr>\n";
}
print "</table>\n";
# ステートメントハンドルクリア
$sth->finish;
# DB切断
$db->disconnect;
print "</body></html>\n";
はじめに全件数を取得する.これはグラフの幅を算出するために全体の人数が必要になるためである.
次にSELECT文でGROUP BY句を指定してグルーピングをかける. ORDER BYにはcount(*)を指定して,件数が多いもの順に出力する. descは「降順」という意味である.
集計が取得できたら今度はグラフの出力を行う.年齢毎のパーセンテージは 「各年齢毎の人数 ÷ 全体の人数 × 100」で計算できるので, その数値を変数$percentに代入する.グラフ幅はこの$percent で算出する.単純にこの$percentを幅としてもいいが,この場合100ピクセル が最大幅となり少し小さいので,ここでは*2を付け加え,最大200ピクセル の幅にしている.グラフイメージの後には,パーセンテージの文字を出力する. int($percent*10+0.5/10は四捨五入のロジックで,少数点1桁で四捨五入を行っている.
graph.cgiにアクセスし,棒グラフを表示させる.