忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2017/03/29 20:11 |
PHP1-15:データベースを使う

今回使用したバージョン。
Pear:MDB2 2.4.1 (stable)
Pear:MDB2_Driver_mysqli 1.4.1 (stable)

昨今何をするにもデータの保存は外せません。
ホームページのデータそれ自身はともかく、ユーザ情報、書き込みログ、買い物や検索のログ、その他諸々のデータはどこかに保存しておかねばなりません。
簡単な一時データならファイルでもいいかもしれませんが、ファイルは一旦全部を読み込まなければ検索すらままならず、また一行だけ書き換えたいというときも全体を書き換えなければならず、そして何と言っても遅いので、あまり使わないほうがよいでしょう。
そこらへんを便利に扱うために登場したのがデータベースです。
検索なら100万件のデータからでも1秒以内で取り出せてしまうという早さ、一行ごとの追加や変更が簡単に行えるという便利さから、Webサービスをはじめあらゆるオンラインシステムに、なくてはならない存在です。
そのかわりSQLというまた面倒な言語を覚えなければいけませんが、まあ単純な追加検索程度なら簡単ですのでさくっといきましょう。


さて、作るといっても一から作ると車輪の再発明です。
自作するよりよっぽど質のいいライブラリが既に出回っているので使わせてもらいましょう。
今回使用するのはPear:MDB2です。

PearはPHPのライブラリで、有志がいろいろと便利なクラスを作成してくれています。
インストールもコマンドラインからpear install MDB2とか書くだけで勝手にインストールまで行ってくれますし、そもそもXAMPPには最初からMDB2が入っているのですが、今回はわざわざ手動でインストールしてみます。

まずPHPのフォルダにpear用フォルダを作成。
C:\xampp\htdocs\src\php\pear\MDB2

次にPearのサイトよりMDB2を取得。
http://pear.php.net/search.php?q=mdb2&in=packages
MDB2とMDB2_Driver_mysqliをダウンロードします。
MDB2はPear:MDB2の本体です。
MDB2_Driver_mysqliはPHP5でのMySQL接続用ファイルです。
PHP4ならMDB2_Driver_mysql、MySQL以外のDBを使用しているなら対応したファイルを取得します。

ダウンロードしたファイルを展開。
package.xmlとかtestsフォルダとかは要りませんので、MDB2フォルダのみを残して以下のようにします。
C:\xampp\htdocs\src\php\pear\MDB2\MDB2.phpおよびMDB2フォルダ
MDB2_Driver_mysqliも同じように展開し、MDB2の同名フォルダに上書きコピーします。

たったこれだけでPear:MDB2を使用する準備が整いました。


使うかどうかわかりませんが、とりあえずMySQLにInnoDBの設定を行っておきます。
このmy.cnfで合ってるのかどうか怪しいのだが、C:\xampp\mysql\bin\my.cnf(おそらくmy:短縮ダイアルと表示されている)のskip-innodbをコメントアウトし、その下のディレクトリのコメントを外します。

 my.cnf

# Comment the following if you are using InnoDB tables
#skip-innodb
innodb_data_home_dir = "C:/xampp/mysql/"
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = "C:/xampp/mysql/"
innodb_log_arch_dir = "C:/xampp/mysql/"


PhpMyAdminからPHPで使用するデータベースおよびテーブルを作成しておきます。
PHPからでも作成できますが、どうせ最初の一回しか使わないので、配布するパッケージでもない限り手動で行ったほうが手っ取り早いです。
 

CREATE DATABASE testdb DEFAULT CHARACTER SET utf8;
GRANT ALL ON testdb.* to testuser@localhost IDENTIFIED BY 'testpass';
USE testdb;
CREATE TABLE books(
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(128) NOT NULL,
  author VARCHAR(128) NOT NULL,
  publisher VARCHAR(128) DEFAULT NULL,
  price int(10) DEFAULT 0,
  isbn VARCHAR(16) DEFAULT NULL,
  date DATE DEFAULT NULL,
  comment TEXT DEFAULT NULL,
  created_at TIMESTAMP NOT NULL default CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT NULL,
  deleted_at DATETIME DEFAULT NULL,
  KEY index_name (name),
  KEY index_author (author)
) ENGINE=InnoDB,DEFAULT CHARACTER SET utf8;


SQLの区切りはPHPと同じく;です。
SQLの命令は小文字でも通るのですが、基本的に大文字で書くことになっています。
逆にテーブル名やカラム名は小文字で統一していたほうが安全です。
MySQLは大文字小文字にはアバウトですが、アバウトが故に問題が発生することがあります。

上記のSQLでは、まずCREATE DATABASEで今回使用するデータベースを作成します。
次にGRANT文で、ID:testuser、パスワード:testpassのユーザに、testdbデータベースに対する全アクセス権を与えています。
drop table等のプログラムから行う必要のない権限も与えてしまっているため、公開する場合はきちんとINSERT、UPDATE等の必要最低限に抑えておきましょう。
最後にCREATE TABLE文でテーブルを作成します。


早速Pear::MDB2を呼び出してみます。
今回は直接書いていますが、define等はコンフィグファイルを作成し、そちらにまとめて書くといいです。

まずはPear:MDB2にアクセスするモデル。

peardb.class.php 

<?php

    define('PEAR_MDB2_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/MDB2/');
    require_once(PEAR_MDB2_DIR.'MDB2.php');

    class pearMDB2Model {
        //メンバ変数
        protected $db_dsn = array(
            'phptype'  => 'mysqli',
            'username' => 'testuser',
            'password' => 'testpass',
            'hostspec' => 'localhost',
            'database' => 'testdb'
        );
        protected $db='';
        protected $res='';

        //接続
        public function __construct(){
            $this->db=MDB2::singleton($this->db_dsn);
            if (PEAR::isError($this->db)) {
                die($this->db->getMessage());
            }
            $this->db->query('SET NAMES UTF8');
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
        }

        //インサート
        public function insert($name,$author,$publisher,$price,$comment,$isbn,$date){
            $sql =' INSERT INTO books ';
            $sql.=' (name,author,publisher,price,isbn,date,comment,created_at) ';
            $sql.=' values ';
            $sql.=' ( ';
            $sql.=$this->db->quote($name,'text');
            $sql.=' ,'.$this->db->quote($author,'text');
            $sql.=' ,'.$this->db->quote($publisher,'text');
            $sql.=' ,'.$this->db->quote($price,'integer');
            $sql.=' ,'.$this->db->quote($isbn,'text');
            $sql.=' ,'.$this->db->quote($date,'text');
            $sql.=' ,'.$this->db->quote($comment,'text');
            $sql.=' ,now() ';
            $sql.=' )';

            $this->res=$this->db->exec($sql);
            if (PEAR::isError($this->res) || $this->res==0){
                return false;
            }else{
                return true;
            }
        }

    //読み取り
        public function getAllBooks(){
            $sql =' select * from books ';
            $sql.=' where deleted_at is null ';

            $ret=$this->db->query($sql);
            $ret=$ret->fetchAll();

            if (PEAR::isError($ret)){
                return false;
            }else{
                return $ret;
            }
        }
    

    #↓クラスのおわり
    }


コントローラ。

 peardb.php

<?php
    require_once('./peardb.class.php');
    $db = new pearMDB2Model();

    $name='恋空〈上〉';
    $author='美嘉';
    $publisher='スターツ出版';
    $price='1050';
    $isbn='978-4883810451';
    $date='2006/10/01';
    $comment='スイーツ(笑)';
    
    $db->insert($name,$author,$publisher,$price,$comment,$isbn,$date);

    $a=$db->getAllBooks();
    print("<pre>");var_dump($a);


$db_dsnはDBに接続するための定型文で、各パラメータを配列で指定します。
一行で指定する書き方もありますが、配列のほうがわかりやすく変更も容易なのでお勧めです。

MDB2::singletonで実際に接続します。
一般的にはMDB2::connectが多く使われますが、singletonのほうが都合がいいのでこちらにしましょう。

setFetchModeでSELECT文を発行した場合の受け取り方を決定します。
デフォルトでは数値の配列で返ってきて非常にわかりにくいのですが、MDB2_FETCHMODE_ASSOCと指定することで連想配列のキーにカラム名を入れて返してきてくれるようになります。

query('SET NAMES UTF8');は、これから送信するデータがUTF-8で書かれているという宣言です。
PHP+MySQLの組み合わせは文字コードに纏わる話題に事欠かず、幾つもの自動変換が入っていたり非常に複雑で面倒なのですが、とりあえず最初にこれを書いておけば基本的に大丈夫です。
勿論受け渡しのデータがUTF-8でかかれていなければ文字化けします。

if(PEAR::isError($this->db))はPEARでエラーが出た場合共通して使用できるコードで、SQLを打った後は基本的にこれで成功したかどうかのチェックを行います。


function insertではデータベースに情報をインサートする処理を記入しています。
沢山の引数を並べるとわかりにくくなるので配列にしたほうがいいのですが、今回はそのままです。

quoteはデータベースにデータを突っ込むときには必須の処理で、SQL上問題になりそうな文字をエスケープしてくれます。
PHP側でもmysql_real_escape_stringやpg_escape_string等の関数はありますが、データベース毎にエスケープ方法が違い、それに伴って使用する関数も違うため、使用するデータベースを変更した場合全ての関数を変更しないといけなくなり非常に面倒です。
まあラッパ関数作ればいいのですが、Pear:MDB2ではそこらへんを最初のDSNに従って自動的に行ってくれるので使わない手はありません。

作成したSQL文はexecで実行を行い、返り値には実行の結果変更された行数が返ってきます。


function getAllBooksではbooksテーブルから全ての値を取得しています。
内容が返ってくるクエリはqueryで実行を行います。
返り値は長大なオブジェクトとなっていてなんだかよくわかりません。
fetchAllとすることで、実際のSQL文の返り値を配列として取得できます。
他にもfetchOneやfetchCol等で返り値の一部分だけを取得することができます。


想定どおりに動けば、insertメソッドでテーブルbooksにデータが挿入され、getAllBooksメソッドでそのデータが取得できるはずです。

さっそく実行。

Catchable fatal error: Object of class MDB2_Error could not be converted to string in C:\xampp\htdocs\src\php\etc\peardb.class.php on line 35

お?


35行目は
$sql.=$this->db->quote($name,'text');
です。

MDB2.phpのソースを追ってましょう。
function quote()内でloadModuleに失敗している

loadModule内でMDB2::fileExistsに失敗している。

fileExists内でis_readableに失敗している。

困ったときのvar_dump、MDB2.phpに一行を挿入して何故失敗しているのかパスを表示してみます。

MDB2.php 

    var_dump($dir . DIRECTORY_SEPARATOR . $file);
    if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {


string(33) ".\MDB2\Driver\Datatype\mysqli.php"
string(50) "C:\xampp\php\pear\\MDB2\Driver\Datatype\mysqli.php"
string(19) ".\MDB2\Datatype.php"
string(36) "C:\xampp\php\pear\\MDB2\Datatype.php"

なんだその\\は。

 peardb.php

var_dump(ini_get('include_path'));


string(20) ".;C:\xampp\php\pear\"

XAMPPがインストールされたときに、include_pathの最後に\を付けてしまっていたのが原因でした。
これ見つけるのに丸一日かかるとかもうね…

というかそれ以前にこれ、include_pathしか見てくれていません。
これでは最初から入っていたほうのPear:MDB2を参照してしまいます。
どうしてMDB2フォルダからの相対パスを見てくれていないのかは謎。

仕方ないのでpeardb.class.phpにinclude_pathを追加。

peardb.class.php 

    public function __construct(){
        ini_set('include_path',PEAR_MDB2_DIR.PATH_SEPARATOR.ini_get('include_path'));
        $this->db= & MDB2::connect($this->db_dsn,'charset=utf8');
        if (PEAR::isError($this->db)) {
            die($this->db->getMessage());
        }
        $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
        $this->db->query('SET NAMES UTF8');
    }


これでとりあえずDBへの書き込み、および表示が出来るようになりました。
めでたしめでたし。

次回は早くて便利だけど使いにくくてわかりにくいプリペアードステートメントの話でも。

PR


2008/08/15 16:16 | Comments(2) | TrackBack(0) | PHP

トラックバック

トラックバックURL:

コメント

SET NAMESは禁止
http://blog.ohgaki.net/set_namesa_mcb_asc
posted by 名無しさん必死だな URL at 2008/08/22 10:27 [ コメントを修正する ]
http://php.benscom.com/manual/ja/function.mysql-set-charset.php
mysql_set_charset関数を使ってみる - PHPプロ!TIPS+
posted by 名無しさん必死だな URL at 2008/08/22 10:35 [ コメントを修正する ]

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<PHP1-16:Pear::MDB2/プリペアードステートメント | HOME | PHP1-14:ファイルに保存クラスの追記>>
忍者ブログ[PR]