2003年度後期 IT教育基礎論演習B
データベースを利用したwebアプリケーションの開発のために、 データベース管理システムとしてPostgreSQLを例にとり、 PHPからのSQLによるデータベース利用方法を学習する。
PHPでは、リレーショナルデータベース管理システムへの接続や問合せ、 問合せ結果の処理など、 リレーショナルデータベースを利用するための関数が提供されている。 これらの関数を利用することにより、 フォームから入力された値に基づくデータベースの問合せや各種データの登録など、 データベースを利用したwebアプリケーションを容易に構築することができる。
ただし、PHPでは、ORACLEやDB2、PostgreSQLなど、 複数種類のデータベース管理システムを利用可能であるが、 そのための関数は各データベース毎に個別に用意されており、 あるデータベース管理システム用に作成したPHPのプログラムを 他のデータベース管理システム用にそのまま利用することはできない。 一般に、リレーショナルデータベース管理システムでは、 SQLに基づく問合せによりデータベースを利用することができるが、 個々のデータベース管理システムにより その接続方法や利用可能なSQLの文法に若干の違いがある。 そのためPHPでも、データベース管理システム毎に 異なる関数を用意する形となっている。 ただし、SQLに基づくリレーショナルデータベース管理システムであれば、 ほぼ同様の関数を利用可能であるため、 各リレーショナルデータベース管理システムの仕様の違いを確認できれば、 プログラムの移植は比較的容易である。
なお、本演習では、 リレーショナルデータベース管理システムとしてPostgreSQLを利用することとする。
データベースを利用したWebアプリケーションを作成するには、 先ず、そのためのデータベースを作成し、 また、具体的なデータを登録するためのテーブルを作成する必要がある。
PostgreSQLで新規データベースを作成するためには、 コマンドラインからcreatedbコマンドを利用する。 createdbコマンドの書式は以下のとおりである。
$ createdb [<オプション> ...] [<データベース名>]
もし、データベース名を省略すると、 その利用者のユーザ名と同じ名前のデータベースが作成される。 例えば、新しく自分のユーザ名と同じ名前のデータベースを作成する場合、 コマンドラインから以下のように入力する。
$ createdb CREATE DATABASE
データベースが正しく作成されているか確認するには、 PostgreSQLへの問合せコマンドであるpsqlコマンドに "-l"オプションをつけて以下のように入力する。
$ psql -l List of databases Name | Owner | Encoding -----------+----------+---------- takoshi | takoshi | EUC_JP template0 | postgres | EUC_JP template1 | postgres | EUC_JP (3 rows)
ただし、同じデータベース管理システム上に 同じ名前のデータベースを作成することはできない。
データベースを作成したら、次に、 具体的なデータを登録するためのテーブルを作成する。 テーブルを作成するためには、SQLによる問合せを行う。
PostgreSQLに対してSQLによる問合せを行うためには、 psqlコマンドを利用する。 psqlコマンドの書式は以下のとおりである。
$ psql [<オプション> ...] [<データベース名> [<ユーザ名>]]
もし、自分が所有するデータベースにアクセスしたい場合には、 ユーザ名を省略することができる。 また、ユーザ名と同じ名前のデータベースにアクセスする場合には、 データベース名も省略できる。 従って、自分の所有する、ユーザ名と同じデータベースにアクセスする場合は、 引数に何も指定せず、psqlコマンドを実行すればよい。 psqlコマンドを実行すると、以下のように簡単な説明が表示された後、 SQL文を入力するためのコマンドプロンプトとして"=>"が表示される。
$ psql Welcome to psql, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help on internal slash commands \g or terminate with semicolon to execute query \q to quit =>
SQLによる問合せにより新規テーブルを作成するには、 create table文を利用する。 create table文の書式は、以下のようになる(途中の改行は入れなくてもよい)。
=> create table <テーブル名> ( | <属性1> <型1> [<オプション1>], | |
<属性2> <型2> [<オプション2>], | ||
... | ||
<属性n> <型n> [<オプションn>] | ); |
例えば、表1に示すスキーマによるテーブル: ei_studentsを作成する場合、 以下のようにSQLを発行する("(>"は、入力継続のためのプロンプトを示す)。
=> create table ei_students ( (> id varchar(8) not null, (> ename varchar(30) not null, (> jname varchar(30), (> age integer, (> gender boolean);
属性 | 型 | 必須/非必須 | 内容 |
---|---|---|---|
id | 文字列(8文字まで) | 必須 | 学籍番号 |
ename | 文字列(30文字まで) | 必須 | 英文氏名 |
jname | 文字列(30文字まで) | 非必須 | 和文氏名 |
age | 整数 | 非必須 | 年齢 |
gender | 真偽値 | 非必須 | 性別(true: 男性 / false: 女性) |
生成されたテーブルに対してデータを登録するには、 SQLのinsert文を使用する。 例えば、テーブルei_studentsに表2に示すデータを登録するには、 以下のように繰り返しinsert文を実行する。
=> insert into ei_students values ( 'a1fm1001', 'Masayuki Torai', '虎井 正之', 34, true); INSERT 41751 1 => insert into ei_students values ( 'a1fm1002', 'Yuji Tameyama', '為山 雄二', 33, true); INSERT 41752 1 => insert into ei_students values ( 'a1fm1003', 'Yasuto Izumikawa', '泉川 靖人', 32, True); INSERT 41753 1 => insert into ei_students values ( 'a1fm1004', 'Yuichi Ogawa', '小河 雄一', 29, true); INSERT 41754 1
id | ename | jname | age | gender |
---|---|---|---|---|
a1fm1001 | Masayuki Torai | 虎井 正之 | 34 | true |
a1fm1002 | Yuji Tameyama | 為山 雄二 | 33 | true |
a1fm1003 | Yasuto Izumiyama | 泉川 靖人 | 32 | true |
a1fm1004 | Yuichi Ogawa | 小河 雄一 | 29 | true |
以下のように、SQLのselect文により 登録されたデータを確認することができる。
=> select * from ei_students; id | ename | jname | age | gender ----------+-------------------+-----------+-----+-------- a1fm1001 | Masayuki Torai | 虎井 正之 | 34 | t a1fm1002 | Yuji Tameyama | 為山 雄二 | 33 | t a1fm1003 | Yasuto Izumikawa | 泉川 靖人 | 32 | t a1fm1004 | Yuichi Ogawa | 小河 雄一 | 29 | t (4 rows)
Webサーバ上で実行されるPHPからデータベースを利用するためには、 先ず、データベース上に登録されている各テーブルの アクセス権を変更する必要がある。
PostgreSQLのように複数の利用者が利用可能なデータベース管理システムでは、 各データベースやテーブルのアクセス権が利用者毎に制限されており、 PostgreSQLでは、標準ではデータベースの作成者しかアクセス権を持たない。 しかしながら、webサーバ上で実行されるPHPのスクリプトの実行者は 各システムで設定されたwebサーバの実行者 (wwwやdaemon、rootといった仮想的な利用者に設定されていることが多い)であり、 データベースの作成者ではないため、 そのままではPHPのスクリプトからテーブルにアクセスすることはできない。
PHPのスクリプトからデータベースにアクセスするためには、 以下の2種類の方法:
ただし、PHPスクリプトの中でユーザIDとパスワードにより利用者認証を行った場合、 その利用者が作成したデータベース上の 全てのテーブルに自由にアクセスできるようになるため、 複数のデータベースを作成し、利用している場合など、 セキュリティ上の問題が生じる可能性がある。 これに対し、アクセス権の変更により webサーバを実行している利用者へアクセス権を与える方法では、 データベース上に登録されているテーブル毎に参照権、更新権、削除権などを データベースの利用目的に応じて細かく設定することができる。 そこで、ここでは、アクセス権の変更方法を紹介する。
Webサーバを実行している利用者にもアクセス権を与えるためには、 先ず、webサーバを実行している利用者をPostgreSQLの利用者として作成する。 例えば、webサーバを実行している利用者が"www"であったとする場合、 PostgreSQLの管理者、もしくは新規利用者登録権限を有する利用者が、 コマンドラインから以下のようにPostgreSQLの利用者として"www"を作成する (注: 本演習では、この作業は予め行なってあるので必要ない )。
$ createuser www Shall the new user be allowed to create databases? (y/n) y Shall the new user be allowed to create more new users? (y/n) n CREATE USER
次に、webサーバからのアクセスを許可するテーブルへのアクセス権を そのテーブルの作成者がwebサーバの利用者に与える。 テーブルへのアクセス権を他の利用者に与えるには、 SQLのgrant文を使用する。 grant文の書式は以下のようになる。
=> grant <アクセス権> on <テーブル名> to <ユーザID> ;
アクセス権には、テーブルへのアクセス方法を指定する。 例えば、webサーバからテーブルei_studentsへの select文によるデータの参照のみを許可する場合、 以下のように、pgsqlコマンドによりPostgreSQLにアクセスした後、 grant文を実行する。
$ psql Welcome to psql, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help on internal slash commands \g or terminate with semicolon to execute query \q to quit => grant select on ei_students to www; GRANT =>
もし、webサーバからテーブルei_studentsを参照するだけでなく、 update文によるデータの更新や delete文による削除も行なえるようにしたい場合には、 以下のように、各アクセス権をコンマで区切って入力する。
=> grant select, update, delete on ei_students to www; GRANT
逆に、他の利用者からのテーブルへのアクセス権を取り上げるには、 SQLのrevoke文を使用する。 revoke文の書式は以下のようになる。
=> revoke <アクセス権> on <テーブル名> from <ユーザID> ;
例えば、webサーバからテーブルei_studentsの update文によるデータの更新や delete文による削除を行なえないようにしたい場合には、 以下のようにrevoke文を実行する。
=> revoke update, delete on ei_students to www; REVOKE
PHPでは、PostgreSQLへ接続し、各種問合せや問合せ結果の処理のために、 様々な関数が用意されており、 これによりPostgreSQLによるwebアプリケーションを開発することができる。 ここでは、その基本的な使用方法について学習する。 詳しくはPHPのマニュアルの PostgreSQL関数の項を参照すること。
データベースにアクセスし、SQLによる各種問合せを行うには、 先ず、データベース管理システムに接続する必要がある。 PHPによりPostgreSQLに接続するには、 pg_connect()関数を利用するする。 pg_connect()関数の書式は以下のようになる。
<接続リソース> = pg_connect(<接続方法指定文字列>);
接続方法指定文字列には、 表3に示す項目とその値を"="で結び、 各指定内容を空白で区切って記述する。 このとき、指定しない項目は省略することができる。 例えば、PHPを実行するホストと同じコンピュータ上で稼動している PostgreSQLにTCP/IP通信を利用せずに接続し、 PHPを実行する利用者の権限でデータベースにアクセスする場合、 hostやport、 user、passwdを指定する必要はない。
項目 | 値 |
---|---|
host | TCP/IP通信により接続する場合のPostgreSQLが稼動するホスト名 |
port | TCP/IP通信により接続する場合の通信ポート |
dbname | アクセスするデータベース名 |
user | dbnameで指定されるデータベースにアクセス権のある利用者名 |
password | userで指定される利用者のパスワード |
pg_connect()関数を実行し、正しく接続されると、 戻り値としてPostgreSQLへの接続リソースであるリソース型の値を得る。 SQLによる問合せは、この接続リソースを介して行う。 もし、データベースへのアクセスができなかった場合、 戻り値は偽(false)となる。 また、問合せを終わり、PostgreSQLとの接続を終了するには、 この接続リソースを引数としてpg_close()関数を利用する。
以上に基づき、例えば、webサーバ上で実行されるPHPスクリプトから、 webサーバと同じコンピュータで稼動するPostgreSQL上の 各自が作成したデータベースにアクセスし、何もせず、接続を終了する場合、 以下のようなプログラムを記述することができる。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { echo("Connect to $dbname<br>"); /* 本来はここにデータベースへの問合せを記述 */ pg_close($c); } else { echo("Cannot connect to $dbname<br>"); } ?> |
PHPスクリプトからPostgreSQLに問合せを行うには、 pg_query()関数を利用する。 pg_query()関数の書式は以下のようになる。
<問合せ結果リソース> = pg_query(<接続リソース>, <問合せ文>);
第1引数には、pg_connect()関数の戻り値として得られる 接続リソースを指定し、 第2引数には、SQLによる問合せ文そのものを指定する。 また、pg_query()関数の戻り値には、 正しく問合せが行われれば、問合せ結果を示すリソースが返り、 問合せが失敗した場合には、偽(false)が返る。
例えば、PostgreSQLに接続し、 表4に示すデータをinsert文により テーブルei_studentsに追加し、 接続を終了する場合、 以下のように記述することができる。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { $query = "insert into ei_students values ('a1fm1005', 'Takoshi Yotsuashi', '四足 蛸士', 33, True);"; $r = pg_query( $c, "$query" ); if ( $r != false ) { echo("Query succeeded<br>"); } else { echo("Query failed<br>"); } pg_close($c); } else { echo("Cannot connect to $dbname<br>"); } ?> |
id | ename | jname | age | gender |
---|---|---|---|---|
a1fm1005 | Takoshi Yotsuashi | 四足 蛸士 | 33 | true |
このプログラムを実行した後、 以下のように、UNIXのコマンドラインからpsqlコマンドを実行し、 select文によりテーブルの内容を確認すると、 正しくデータを追加できていることがわかる。
=> select * from ei_students; id | ename | jname | age | gender ----------+-------------------+-----------+-----+-------- a1fm1001 | Masayuki Torai | 虎井 正之 | 34 | t a1fm1002 | Yuji Tameyama | 為山 雄二 | 33 | t a1fm1003 | Yasuto Izumikawa | 泉川 靖人 | 32 | t a1fm1004 | Yuichi Ogawa | 小河 雄一 | 29 | t a1fm1005 | Takoshi Yotsuashi | 四足 蛸士 | 33 | t (5 rows)
同様に、select文による問合せにより テーブルの内容を参照するには、 以下のようなプログラムを記述することができる。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { $query = "select * from ei_students;"; $r = pg_query( $c, "$query" ); if ( $r != false ) { echo("Query succeeded<br>"); } else { echo("Query failed<br>"); } pg_close($c); } else { echo("cannot connect to $dbname<br>"); } ?> |
ただし、このプログラムでは、 SQLによる問合せの結果はリソース型の変数$rに格納されるだけであり、 その内容を直接参照することはできない。
なお、PHPでは、このようなSQL文を直接利用する低レベルな関数のほかに、 配列のテーブルへの挿入や、テーブルからの配列の生成などを行う 高レベルな関数も用意されているが、 これらの関数はまだ実験段階にあり、その仕様が確定していない。 PHPの今後の発展が期待されるところである。
PHPでは、select文による問合せの結果として得られるテーブルは、 特別なデータ型であるリソース型の値として得られ、 これを直接画面に表示することはできない。 これは、データベースに登録されているデータが必ずしも数値や文字列だけではなく、 その結果、問合せの結果として得られる表も、 そのまま画面に表示可能なものとは限らないためである。
画面への表示(HTMLデータとしての出力)など、 問合せ結果として得られた表を処理するためには、 問合せ結果の個々の要素を取り出し、 そのデータ型に応じて適切な処理を行わなければならない。 そのためにPHPでは、問合せ結果の処理のために様々な関数が用意されている。 以下、その簡単な使用方法について触れる。
問合せ結果を処理するためには、先ず、 得られた表に何行あるかを確認する必要がある。 表の行数を確認するためには、 pg_num_rows()関数を利用する。 pg_num_rows()関数の書式は以下のようになる。
<整数> = pg_num_rows(<接続リソース>);
例えば、データベースにアクセスし、 テーブルei_studentsの全てのデータを取得した結果、 何行あるかを表示するプログラムは以下のように記述できる。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { $query = "select * from ei_students;"; $r = pg_query( $c, "$query" ); if ( $r != false ) { $n = pg_num_rows( $r ); echo("Retrieved $n rows<br>"); } else { echo("Query failed<br>"); } pg_close($c); } else { echo("cannot connect to $dbname<br>"); } ?> |
PHPでは、表のn行目の行(先頭行は0行目となる)を取り出し、 その要素を配列に格納する関数として、 pg_fetch_row()関数やpg_fetch_assoc()関数が 用意されている。 pg_fetch_row()関数では、 取り出した行の各属性の値を順番に配列に格納する。 これに対しpg_fetch_assoc()関数では、 取り出した行の各属性値をその属性名をキーとする連想配列に格納する。
表中の全ての行を取り出したい場合、 これらの関数をその行数分だけ繰り返し実行すればよい。 例えば、テーブルei_studentsに登録されている全てのデータを そのまま表形式で出力するには、 以下のようなプログラムを記述することができる。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { $query = "select * from ei_students;"; if ( $r = pg_query( $c, "$query" ) ) { $m = pg_num_rows($r); echo("<table border=\"1\">"); for( $i = 0; $i < $m; $i++ ) { echo("<tr>"); $row = pg_fetch_row($r, $i); foreach($row as $value) { echo("<td>$value</td>"); } echo("</tr>"); } echo("</table>"); } else { echo("Query failed<br>"); } pg_close($c); } else { echo("Cannot connect to $dbname<br>"); } ?> |
これを実行した結果は、以下のようになる。
a1fm1001 | Masayuki Torai | 虎井 正之 | 34 | t |
a1fm1002 | Yuji Tameyama | 為山 雄二 | 33 | t |
a1fm1003 | Yasuto Izumiyama | 泉川 靖人 | 32 | t |
a1fm1004 | Yuichi Ogawa | 小河 雄一 | 29 | t |
a1fm1005 | Takoshi Yotsuashi | 四足 蛸士 | 33 | t |
この例では、実行結果として得られたテーブルの内容を そのまま表形式に出力しているが、 これでは、各列の項目が判らないばかりでなく、 性別を真偽値で示しているため、知らない人が見た場合には、 全く意味が判らなくなってしまう。 そこで、pg_fetch_assoc()関数を利用して属性名を取得し、 表の見出しをつけるとともに、 性別も対応する文字列として出力するようにプログラムを書き改めると、 以下のようになる(注: PostgreSQLにおける真偽値("True" / "False")は、 PHPでは、現在のところ文字列("t" / "f")として扱われる)。
<?php $dbname="a3fmxxxx"; /* 各自が作成したデータベース名を指定 */ $c = pg_connect("dbname=$dbname"); if ( $c != false ) { $query = "select * from ei_students;"; if ( $r = pg_query( $c, "$query" ) ) { $m = pg_num_rows($r); echo("<table border=\"1\">"); echo("<tr>"); $row = pg_fetch_assoc($r, 0); foreach($row as $key => $value) { echo("<th>"); switch( $key ) { case "id": echo("ID"); break; case "ename": echo("英文氏名"); break; case "jname": echo("和文氏名"); break; case "age": echo("年齢"); break; case "gender": echo("性別"); break; } echo("</th>"); } echo("</tr>"); for( $i = 0; $i < $m; $i++ ) { echo("<tr>"); $row = pg_fetch_assoc($r, $i); foreach($row as $key => $value) { echo("<td>"); if ( $key == "gender" ) { if ( $value == "t" ) { echo("男性"); } else { echo("女性"); } } else { echo("$value"); } echo("</td>"); } echo("</tr>"); } echo("</table>"); } else { echo("Query failed<br>"); } pg_close($c); } else { echo("Cannot connect to $dbname<br>"); } ?> |
この実行結果は、以下のようになる。
ID | 英文氏名 | 和文氏名 | 年齢 | 性別 |
---|---|---|---|---|
a1fm1001 | Masayuki Torai | 虎井 正之 | 34 | 男性 |
a1fm1002 | Yuji Tameyama | 為山 雄二 | 33 | 男性 |
a1fm1003 | Yasuto Izumiyama | 泉川 靖人 | 32 | 男性 |
a1fm1004 | Yuichi Ogawa | 小河 雄一 | 29 | 男性 |
a1fm1005 | Takoshi Yotsuashi | 四足 蛸士 | 33 | 男性 |
表1に示したスキーマによるテーブルei_studentsを作成し、 学生を5名以上登録せよ。
次に、各学生における各科目(A〜E)の進捗率を登録するために、 表5に示すスキーマによるテーブルei_progressを作成し、 各学生の進捗率を登録せよ。
属性 | 型 | 必須/非必須 | 内容 |
---|---|---|---|
id | 文字列(8文字まで) | 必須 | 学籍番号 |
A | 整数(1〜100) | 非必須 | 科目Aの進捗率(%) |
B | 整数(1〜100) | 非必須 | 科目Bの進捗率(%) |
... | ... | ... | ... |
E | 整数(1〜100) | 非必須 | 科目Eの進捗率(%) |
レポートには、 psqlによるコマンドラインからselect文を実行し、 登録した内容を表示した結果を示せ。
各学生の学籍番号とその学生の各科目の進捗率を表形式にしてwebページに出力せよ。 レポートには、PHPのプログラムおよび出力結果を示せ。
フォームから学籍番号、英文氏名、和文氏名、年齢、性別を入力すると、 ei_studentsおよびei_progressの両テーブルに 新規学生を追加し、 学生一覧と各学生の進捗率を出力するwebページを作成せよ。 ただし、各科目の進捗率の初期値は0%とする。 また、学生一覧と進捗率は別の表としてもよい。 レポートには、フォーム入力のためのHTMLファイル、PHPのプログラム、 および入力画面と出力結果を示せ。
フォームから学籍番号を入力すると、 webページ上に対応する学生の学籍番号および和文氏名を出力し、 また、その学生の各科目の進捗率を棒グラフと数値により出力する webページを作成せよ。 レポートには、フォーム入力のためのHTMLファイル、PHPのプログラム、 および入力画面と出力結果を示せ。