001.中間層PHPの構造と記述方法
この項では
中間層PHPである
common/contents_db.phpについて説明します。
中間層PHPとは具体的には
SELECT文を発行するクラスです。
INSERT、UPDATE、DELETEは
中間層PHPは一般的には個々には記述しません。これら
更新系のSQLについては後述します。
これから説明するようなクラスは、実際にPHPでサイトを作成する上で、各自が記述する必要があります。このドキュメントではサンプルとして
都道府県テーブル用のクラスが記述されています。
この項では、
都道府県テーブル用のクラスを説明することで、記述方法の説明とします。
コンテンツDBクラス
common/contents_db.phpをエディタで開くと以下のようなクラスがあります。
//--------------------------------------------------------------------------------------
/// 都道府県クラス
//--------------------------------------------------------------------------------------
class cprefecture extends crecord {
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ
*/
//--------------------------------------------------------------------------------------
public function __construct() {
//親クラスのコンストラクタを呼ぶ
parent::__construct();
}
//--------------------------------------------------------------------------------------
/*!
@brief すべての個数を得る
@param[in] $debug デバッグ出力をするかどうか
@return 個数
*/
//--------------------------------------------------------------------------------------
public function get_all_count($debug){
//親クラスのselect()メンバ関数を呼ぶ
$this->select(
$debug, //デバッグ文字を出力するかどうか
"count(*)", //取得するカラム
"prefecture", //取得するテーブル
"1" //条件
);
if($row = $this->fetch_assoc()){
//取得した個数を返す
return $row['count(*)'];
}
else{
return 0;
}
}
//--------------------------------------------------------------------------------------
/*!
@brief 指定された範囲の配列を得る
@param[in] $debug デバッグ出力をするかどうか
@param[in] $from 抽出開始行
@param[in] $limit 抽出数
@return 配列(2次元配列になる)
*/
//--------------------------------------------------------------------------------------
public function get_all($debug,$from,$limit){
$arr = array();
//親クラスのselect()メンバ関数を呼ぶ
$this->select(
$debug, //デバッグ表示するかどうか
"*", //取得するカラム
"prefecture", //取得するテーブル
"1", //条件
"prefecture_id asc", //並び替え
"limit " . $from . "," . $limit //抽出開始行と抽出数
);
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
//取得した配列を返す
return $arr;
}
//--------------------------------------------------------------------------------------
/*!
@brief 指定された範囲の配列を得る(プレースホルダつき版)
@param[in] $debug デバッグ出力をするかどうか
@param[in] $from 抽出開始行
@param[in] $limit 抽出数
@return 配列(2次元配列になる)
*/
//--------------------------------------------------------------------------------------
public function get_all_prep($debug,$from,$limit){
$arr = array();
$query = <<< END_BLOCK
select
*
from
prefecture
where
1
order by
prefecture_id asc
limit ?, ?
END_BLOCK;
$prep_arr = array($from,$limit);
//親クラスのselect_query()メンバ関数を呼ぶ
$this->select_query(
$debug, //デバッグ表示するかどうか
$query, //プレースホルダつきSQL
$prep_arr //データの配列
);
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
//取得した配列を返す
return $arr;
}
//--------------------------------------------------------------------------------------
/*!
@brief 指定されたIDの配列を得る
@param[in] $debug デバッグ出力をするかどうか
@param[in] $id ID
@return 配列(1次元配列になる)空の場合はfalse
*/
//--------------------------------------------------------------------------------------
public function get_tgt($debug,$id){
if(!is_int($id)
|| $id < 1){
//falseを返す
return false;
}
//親クラスのselect()メンバ関数を呼ぶ
$this->select(
$debug, //デバッグ表示するかどうか
"*", //取得するカラム
"prefecture", //取得するテーブル
"prefecture_id=" . $id //条件
);
return $this->fetch_assoc();
}
//--------------------------------------------------------------------------------------
/*!
@brief 指定されたIDの配列を得る(プレースホルダつき版)
@param[in] $debug デバッグ出力をするかどうか
@param[in] $id ID
@return 配列(1次元配列になる)空の場合はfalse
*/
//--------------------------------------------------------------------------------------
public function get_tgt_prep($debug,$id){
if(!is_int($id)
|| $id < 1){
//falseを返す
return false;
}
$query = <<< END_BLOCK
select
*
from
prefecture
where
prefecture_id = ?
END_BLOCK;
$prep_arr = array($id);
//親クラスのselect_query()メンバ関数を呼ぶ
$this->select_query(
$debug, //デバッグ表示するかどうか
$query, //プレースホルダつきSQL
$prep_arr //データの配列
);
return $this->fetch_assoc();
}
//--------------------------------------------------------------------------------------
/*!
@brief デストラクタ
*/
//--------------------------------------------------------------------------------------
public function __destruct(){
//親クラスのデストラクタを呼ぶ
parent::__destruct();
}
}
このクラスは、
都道府県テーブルの
SELECT文用のクラスです。
都道府県テーブルは
prefectureという名前です。
prefectureをSELECTするクラスなので
cprefectureクラスとしています。
このドキュメントでは、このようなクラスを
コンテンツDBクラスと称します。
コンテンツDBクラスは、必ず
crecordクラスを継承して作成します。
prefectureテーブルのようなテーブルを
マスタテーブルといいます。
日常的に変更が行われることが少ないテーブルです。
マスタテーブルは通常は管理画面などで
追加や更新ができるようになっています。
単純なマスタテーブル用のSQL文は、
総レコードの数を得る、
指定したレコードから何行かを取り出す、
指定したレコードを取り出すの3つのアクセス方法があれば足りる場合が多いと思います。
SELECT文を発行する
cprefectureクラスはこららのSQLに対応するメンバ関数が記述されています。
まず、
総レコードの数を得るは
get_all_count()関数です。以下に抜き出します。
//--------------------------------------------------------------------------------------
/*!
@brief すべての個数を得る
@param[in] $debug デバッグ出力をするかどうか
@return 個数
*/
//--------------------------------------------------------------------------------------
public function get_all_count($debug){
//親クラスのselect()メンバ関数を呼ぶ
$this->select(
$debug, //デバッグ文字を出力するかどうか
"count(*)", //取得するカラム
"prefecture", //取得するテーブル
"1" //条件
);
if($row = $this->fetch_assoc()){
//取得した個数を返す
return $row['count(*)'];
}
else{
return 0;
}
}
まず、この関数の引数は
$debugです。これは
発行されたSQL文を表示するかどうかのフラグで、これをtrueにするとSQL文をechoします。
続いて
$this->select(...);と、親クラスの
select()関数を呼び出します。この関数は
common/pdointerface.phpに記述があり、以下のような内容になっています。
//--------------------------------------------------------------------------------------
/*!
@brief select文の実行
@param[in] $debug クエリを出力するかどうか
@param[in] $columns 取得するカラム
@param[in] $table 取得するテーブル
@param[in] $where 条件文(省略可)
@param[in] $orderby ならび順(省略可)
@param[in] $limit 抽出範囲(省略可)
@return 成功すればtrue
*/
//--------------------------------------------------------------------------------------
public function select($debug,$columns,$table,$where = '1',$orderby = '',$limit = ''){
global $DB_PDO;
$this->free_res();
if($orderby != ""){
$orderby = "order by " . $orderby;
}
$this->retarr['sql'] =<<< END_BLOCK
select
{$columns}
from
{$table}
where
{$where}
{$orderby}
{$limit}
END_BLOCK;
//親クラスのcrecord_core_pres関数を呼ぶ(値は渡さない)
$ret = $this->crecord_core_pres(array());
if($debug){
echo '<br />[sql debug]<br />';
$this->res->debugDumpParams();
echo '<br />[/sql debug]<br />';
}
return $ret;
}
赤くなっているところが
SELECT文を組み立てているところです。
SELECT文は、一般的には
select
抽出するカラム
from
テーブル
where
条件式
[ソート順]
[limit句]
となります。これを、関数内で組み立てていることになります。
ただ一つだけ問題なのが
のような場合です。
whereは省略可能です。
しかし、このSQLは
select
*
from
prefecture
where
1
と記述することも可能です。
1というのは
真という意味があり
すべてということになります。
ですから
cprefecture::get_all_count()関数が作り出すSQL文は
select
count(*)
from
prefecture
where
1
となります。
count(*)は、SQLの総数を返す内部コマンドです。
また
select関数が呼び出す
$this->crecord_core_pres(array());の
array()は
プリペアードステートメントを
使わない呼び出し方となります。
プリペアードステートメントを使う場合は、のちに説明する
select_query関数を使ってください。
SELECT文を発行する
このように
$this->select(...);を実行することによりSQL文が発行されます。正常にリソースが取得できると、
if($row = $this->fetch_assoc()){
//取得した個数を返す
return $row['count(*)'];
}
else{
return 0;
}
と
$this->fetch_assoc()関数呼び出しで
1行分の配列データを取得します。この関数は、内部的には
PDOStatement::fetch_assoc()関数を呼び出します。
この関数は取得できない場合
FALSEを返します。
このように、
cprefecture::get_all_count()関数の戻り値は
行数になります。
一覧表示用のメンバ関数
一覧表示に使用されるSELECT文は
cprefecture::get_all()関数で実装します。
SELECT文発行のメカニズムは上記と変わりありません。1つ注意点は、この関数は
何行目から何行分を取り出すという設計になっています。これは、
ページ分けするのに必要な処理です。
//--------------------------------------------------------------------------------------
/*!
@brief 指定された範囲の配列を得る
@param[in] $debug デバッグ出力をするかどうか
@param[in] $from 抽出開始行
@param[in] $limit 抽出数
@return 配列(2次元配列になる)
*/
//--------------------------------------------------------------------------------------
public function get_all($debug,$from,$limit){
$arr = array();
//親クラスのselect()メンバ関数を呼ぶ
$this->select(
$debug, //デバッグ表示するかどうか
"*", //取得するカラム
"prefecture", //取得するテーブル
"1", //条件
"prefecture_id asc", //並び替え
"limit " . $from . "," . $limit //抽出開始行と抽出数
);
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
//取得した配列を返す
return $arr;
}
このようなパラメータになっています。
$fromと
$limitを受け取ります。この値を設定するのは
一覧ページである
prefecture_list.phpで行いますので、
ページ分けについては、一覧ページの説明で行います。
ここでは
cprefecture::get_all()関数は
$fromと
$limitを受け取るように作成しておく、ということだけ確認しておいてください。
なお
cprefecture::get_all()関数は2次元配列を返します。
cprefecture::get_all_count()関数の形とは違います。受け取ったほうはその2次元配列をもとに
HTMLに書式化してechoする形になります。
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
の部分で2次元配列を作成しています。
プリペアードステートメントの使用
PHPBaseは
PDOのプリペアードステートメントも使えます。使用するには
get_all_prep関数にように記述するのがいいでしょう。
//--------------------------------------------------------------------------------------
/*!
@brief 指定された範囲の配列を得る(プレースホルダつき版)
@param[in] $debug デバッグ出力をするかどうか
@param[in] $from 抽出開始行
@param[in] $limit 抽出数
@return 配列(2次元配列になる)
*/
//--------------------------------------------------------------------------------------
public function get_all_prep($debug,$from,$limit){
$arr = array();
$query = <<< END_BLOCK
select
*
from
prefecture
where
1
order by
prefecture_id asc
limit ?, ?
END_BLOCK;
$prep_arr = array($from,$limit);
//親クラスのselect_query()メンバ関数を呼ぶ
$this->select_query(
$debug, //デバッグ表示するかどうか
$query, //プレースホルダつきSQL
$prep_arr //データの配列
);
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
//取得した配列を返す
return $arr;
}
ここでは
SELECT文をすべて記述しています。赤くなってるところが
プレースホルダの指定です。
?を使う形で記述します。そして
$prep_arr = array($from,$limit);
と値の配列を作り出し、
$this->select_query(
$debug, //デバッグ表示するかどうか
$query, //プレースホルダつきSQL
$prep_arr //データの配列
);
と、親クラスの
select_query関数を呼び出します。
select_query関数は
SQL文をすべて記述して呼び出します。必要であればその中に
?を使う
プレースホルダも記述し、値の配列を渡します。
プレースホルダを使わない場合は
//--------------------------------------------------------------------------------------
/*!
@brief 指定された範囲の配列を得る(プレースホルダつき版)
@param[in] $debug デバッグ出力をするかどうか
@param[in] $from 抽出開始行
@param[in] $limit 抽出数
@return 配列(2次元配列になる)
*/
//--------------------------------------------------------------------------------------
public function get_all_prep($debug,$from,$limit){
$arr = array();
$query = <<< END_BLOCK
select
*
from
prefecture
where
1
order by
prefecture_id asc
limit {$from}, {$limit}
END_BLOCK;
//親クラスのselect_query()メンバ関数を呼ぶ
$this->select_query(
$debug, //デバッグ表示するかどうか
$query //プレースホルダつきSQL
);
//順次取り出す
while($row = $this->fetch_assoc()){
$arr[] = $row;
}
//取得した配列を返す
return $arr;
}
のように記述しても大丈夫です。
詳細表示用のメンバ関数
詳細表示用のメンバ関数は
詳細ページ(prefecture_detail.php)で使用しする
cprefecture::get_tgt()関数になります。この関数はパラメータに
プライマリキー(prefecture_id)を渡します。SELECT文を発行する前に、パラメータの有効性(数字でなおかつ1以上かどうか)をチェックしています。
このチェックに使っている
is_int()関数は
整数かどうかを判別する関数です。
PHPBaseの考え方
以上、ざっとですが
コンテンツDBクラスである
cprefectureクラスについて説明しました。
PHPBaseの考え方は、このような
コンテンツDBクラスを記述する作業と、
prefecture_list.phpやprefecture_detail.phpを記述する作業を分けることで、
コードの階層化を図ろう、ということです。
各テーブルに対して(あるいはリレーショナルで同時にSELECT文に含めるテーブルのグループに対して)、
コンテンツDBクラスを記述することで、上層部のコードからは
SQL文を意識しないで記述ができるようになります。
チームで組む場合は、例えば
プログラムリーダー的な人が
コンテンツDBクラスを担当し、それ以外に人が各ページを担当すれば、SQLの発行はプログラムリーダーが組む
コンテンツDBクラスからしかできない形になります。
こうしておけば、突然、クライアントの要求でテーブルの内容を変える必要が出てきたとしても、上層部のプログラマは意識しないですみます。テーブル修正によるSQL文の修正なども
プログラムリーダーが行えばいい形になります。