100.サンプル概要と中間クラス

 この章では住所録サンプルを紹介します。メンバー管理というサンプルです。
 まず、前章のようにテーブル作成を行います。sqlディレクトリmysql_member_sql.txtというファイルがありますので、その中身をすべてphpMyAdminもしくはコマンドラインから実行してください。
 正常にいくつかのテーブルが作成できたら、前章で紹介したメインメニューのページからメンバー管理にリンクがあります。それをクリックすると、以下のような画面が出てきます。(テーブルができてないとメンバー管理には行けませんので注意してください)

 

図0100a

 

 このサンプルには初期データは入ってません。新規をクリックすると詳細ページに行けます。以下が新規の詳細ページです。

 

図0100b

 

 ここには都道府県管理にはなかった入力フィールドが並んでいます。メンバー名市区郡町村以下1行テキストですから都道府県管理にありましたが、ほかの入力は初めて出てきます。
 このサンプルの目的はこれらの入力フィールドを、PHPBaseでは、どのように実現しているのかを、説明するサンプルです。

contents_db.phpへの追加

 メンバー管理の中間クラスは都道府県管理同様contents_db.phpに記述されています。
//--------------------------------------------------------------------------------------
/// フルーツクラス
//--------------------------------------------------------------------------------------
class cfruits extends crecord {
    //--------------------------------------------------------------------------------------
    /*!
    @brief  コンストラクタ
    */
    //--------------------------------------------------------------------------------------
    public function __construct() {
        //親クラスのコンストラクタを呼ぶ
        parent::__construct();
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  すべての個数を得る
    @param[in]  $debug  デバッグ出力をするかどうか
    @return 個数
    */
    //--------------------------------------------------------------------------------------
    public function get_all_count($debug){
        return $this->get_all_count_core($debug,'fruits');
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  指定された範囲の配列を得る
    @param[in]  $debug  デバッグ出力をするかどうか
    @return 配列(2次元配列になる)
    */
    //--------------------------------------------------------------------------------------
    public function get_all($debug){
        return $this->get_alltable_core($debug,'fruits','fruits_id');
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  指定されたIDの配列を得る
    @param[in]  $debug  デバッグ出力をするかどうか
    @param[in]  $id     ID
    @return 配列(1次元配列になる)空の場合はfalse
    */
    //--------------------------------------------------------------------------------------
    public function get_tgt($debug,$id){
        return $this->get_tgt_core($debug,$id,'fruits','fruits_id');
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  デストラクタ
    */
    //--------------------------------------------------------------------------------------
    public function __destruct(){
        //親クラスのデストラクタを呼ぶ
        parent::__destruct();
    }
}

//--------------------------------------------------------------------------------------
/// メンバークラス
//--------------------------------------------------------------------------------------
class cmember extends crecord {
    //--------------------------------------------------------------------------------------
    /*!
    @brief  コンストラクタ
    */
    //--------------------------------------------------------------------------------------
    public function __construct() {
        //親クラスのコンストラクタを呼ぶ
        parent::__construct();
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  すべての個数を得る
    @param[in]  $debug  デバッグ出力をするかどうか
    @return 個数
    */
    //--------------------------------------------------------------------------------------
    public function get_all_count($debug){
        //親クラスのselect()メンバ関数を呼ぶ
        $this->select(
            $debug,                 //デバッグ文字を出力するかどうか
            "count(*)",             //取得するカラム
            "member,prefecture",            //取得するテーブル
            "member.prefecture_id = prefecture.prefecture_id" //条件
        );
        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,         //デバッグ表示するかどうか
            "member.*,prefecture.prefecture_name", //取得するカラム
            "member,prefecture",    //取得するテーブル
            "member.prefecture_id = prefecture.prefecture_id", //条件
            "member.member_id asc", //並び替え
            "limit " . $from . "," . $limit     //抽出開始行と抽出数
        );
        //順次取り出す
        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(!cutil::is_number($id)
        ||  $id < 1){
            //falseを返す
            return false;
        }
        //親クラスのselect()メンバ関数を呼ぶ
        $this->select(
            $debug,         //デバッグ表示するかどうか
            "member.*,prefecture.prefecture_name",          //取得するカラム
            "member,prefecture",    //取得するテーブル
            "member.member_id ={$id} and 
member.prefecture_id = prefecture.prefecture_id"    //条件
        );
        return $this->fetch_assoc();
    }
    //--------------------------------------------------------------------------------------
    /*!
    @brief  フルーツとのマッチする配列を得る
    @param[in]  $debug  デバッグ出力をするかどうか
    @param[in]  $id     ID
    @return 配列(1次元配列になる)
    */
    //--------------------------------------------------------------------------------------
    public function get_all_fruits_match($debug,$id){
        $arr = array();
        //親クラスのselect()メンバ関数を呼ぶ
        $this->select(
            $debug,         //デバッグ表示するかどうか
            "*", //取得するカラム
            "fruits_match", //取得するテーブル
            "member_id = {$id}", //条件
            "fruits_id asc" //並び替え
        );
        //順次取り出す
        while($row = $this->fetch_assoc()){
            $arr[] = $row['fruits_id'];
        }
        //取得した配列を返す
        return $arr;
    }

    //--------------------------------------------------------------------------------------
    /*!
    @brief  デストラクタ
    */
    //--------------------------------------------------------------------------------------
    public function __destruct(){
        //親クラスのデストラクタを呼ぶ
        parent::__destruct();
    }
}
 ここではメンバーのクラスであるcmemberのほかに好きな果物のクラスのcfruitsも記述されています。また、cmemberには都道府県IDであるprefecture_idを利用してプルダウン作成のための記述がされています。
 もうひとつ、get_all_fruits_match()関数複数選択チェックボックス用の関数です。mysql_member_sql.txtに記述があるfruits_matchというテーブルのアクセス関数もあります。

フルーツクラス

 フルーツクラスはcfruitsです。これはメニューには入れてないので追加や更新はHPからはできません。もし必要なら、都道府県管理のようにマスタテーブルとして扱えば実装できます。
 またcfruitsを見ていただくとわかるのですが、cprefectureとは違い
    public function get_all_count($debug){
        return $this->get_all_count_core($debug,'fruits');
    }
 のように、$this->get_all_count_core(...);といった呼び出しが行われています。これは親クラスにあるサービス的な(ナマケモノ向きな)関数です。こう記述するとfruitsテーブルの総数が返ります。
 get_all()やget_tgt()にもコア関数がありますので、単純なマスタテーブルなら利用可能です(ちょっとでも複雑なら、select関数を呼んでください)。

メンバークラス

 メンバークラスはcmemberですが、ここに等価結合を実装してます。住所の都道府県の部分は、都道府県IDであるprefecture_idを持っていて、get_all()関数get_tgt()関数ではprefectureテーブルも参照してます。都道府県名をじかに記述してもらうと福島けんとかふくしま県とか記述が統一されなかったときに困りますからプルダウンで選んでもらうようにしているわけです。
 また、ではリレーションシップがっちり手を結ばせたらいいのではと思うかもしれませんが、経験上、結構、開発途中でのテーブルの変更は行われることは多々あり、開発後半にリレーションシップにすることはありますが、基本的に、僕は切り離しておきます(まあ、現場による、対応ということですね)。
 cmemberにはもう一つget_all_fruits_match()関数があります。この関数はmemberテーブルは参照してません。
 何をしているかというとfruits_matchテーブルを参照して、メンバーIDでSELECTしてます。fruits_matchテーブルは、member_id と fruits_idが対になってるテーブルです。重複OKのテーブルで、member_idをキーにして参照するとそのメンバーが答えた好きなフルーツの一覧が取得できるようにしておきます。
 たぶんなのですが、複雑なクエリを実行することで、get_tgt()関数好きなフルーツも取得することは可能とは思うのですが、僕自身の力量不足とSQLのマスターを目指しているわけではないと理由で、別々に取得しています。

 なお、SELECT系を記述する親クラスcrecordにはquery()関数があり、複雑なSQLはこちらで記述してください。

 このように、SQLのSELECT関連の操作は、contents_db.phpに記述することが可能だと思います。もしcontents_db.phpがファイルが大きくなってしまう場合は、例えばcontents_db2.phpといったファイルに記述してlibs.phpの最後に
require_once("contents_db2.php");
 のように記述します。