こんにちはtakaです。
PHPを学習していく上で切っても切れない「データベース」との関係。
PDOやらtry~catchやら、最初に見た時は「何を言ってるの?」と感じた方、多いと思います。もちろん僕も。
今回の記事ではそんな初心者にとってはとっつきにくいPHPでのデータベース接続から、実際にデータを操作するまでの流れを見ていき、「何が行われているのか?」を一つずつ追っていきます。
この記事を読むことで、呪文しか見えなかったPDOや変数の意味がわかり、データベースの理解が深まりますよ。
まずはデータベースを作成する。
PHPでデータベースに接続するためには、そのデータベースを作成しないといけません。
ここでは、phpMyadminで「php_sample01」というデータベースを作成し、その中に「users」というテーブルを作りました。
テーブルは次のように作成してます。
このデータベースへ接続する方法と、データの操作を見ていきます。
PDOを使ってデータベースへ接続する
そもそもPDOってなに?
作成したデータベースにPHPからアクセスするには、この「PDO」というものを使います。
はい、まず早速この「PDO」って何か?ということです。
PDO(PHP Data Objects) は、データアクセスの抽象化レイヤを提供します。 つまり、使用しているデータベースが何であるかにかかわらず、同じ 関数を使用してクエリの発行やデータの取得が行えるということです。
https://www.php.net/manual/ja/intro.pdo.phpより引用
・・・・全然意味がわかりませんね。
簡単にいうと、データベースというのは今回使用している「MySQL」以外にも「PostgreSQL」や「Oracle」というものがあります。
本来は、上記のデータベースにアクセスするにはそれぞれ別々の方法(関数)でデータベースにアクセスする必要があるのですが、PDOくんはこうしたデータベース間の違いをまとめて操作できるようにしてくれるやつ、という感じです。
どうやってデータベースに接続するの?
PDOを使ってデータベースに接続するには、下記のようにします。
$dsn = 'mysql:dbname=php_sample01;host=localhost;charset=utf8';
$username = 'root';
$password = 'root';
$options = array(
PDO::ATTR_ERRMODE =>PDO::ERRMODE_SILENT,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
);
$dbh = new PDO($dsn, $username, $password, $options);
$dbh
データベースハンドラの略。ここでの変数は任意で決めることができるが、「データベースを操作する(ハンドルする)ための変数として、このような名前をつけてます。
$dsn
データソースネームの略。ここでは「どんなデータベースに接続して、そのホストサーバーはどれか、文字コードは何か?」という情報を記述する。上記の例だと、「php_sample01というデータベースに接続して、localhostというサーバーで、文字コードはUTF-8」としてます。
*この$dsnでやりがちなミス三つ!
② 各情報との間は、「;」セミコロンで区切る。「:」コロンで区切るとエラー出ます!
③ 「utf8」は「utf-8」としないこと!ついやりがちですが気をつけてください。
$username, $password
今回の例でいうと、phpMyadminにログインする時のユーザー名とパスワードを入れる部分。デフォルトだとどちらも「root」となっている。
$options
ここがPDOを接続する上で、厄介な部分です。僕も初めて見た時に、「なんじゃこりゃ??」となりました。
ここは何をしているのかというと、文字通りPDOのオプション(属性)を選択しています。
例えば、「PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT」の部分は「エラーモード→サイレントにしてください(エラーがあっても何も報告せず、エラーコードだけ表示してください」という意味になります。
今回使用しているオプション設定は下記のような種類があります。
https://qiita.com/mpyw/items/b00b72c5c95aac573b71#pdoattr_errmodeより引用。
その他のオプションはこちらの公式ドキュメントをご参照ください。
データベースに接続する時の「try-catch」は何をしているのか?
データベースへ接続するには、上記のようにPDOを用いて接続をしていきますが、実際には下記のように「try-catch」というブロックで囲んでコードを記述します。
try {
$dsn = 'mysql:dbname=php_sample01;host=localhost;charset=utf8';
$username = 'root';
$password = 'root';
$options = array(
PDO::ATTR_ERRMODE =>PDO::ERRMODE_SILENT,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
);
$dbh = new PDO($dsn, $username, $password, $options);
} catch (Exception $e) {
error_log('エラー発生:' . $e->getMessage());
なんでこんなことをするのか、と最初思ってたんですが結局この役割は「何か不測の事態(例外)が発生した時に、その原因を特定するために開発者に知らせたり、またエンドユーザーにそれをアナウンスするため」という、すごく大事な役割りを担っているんだな、と後々になって感じました。
上記の例でいうと、メインの処理(try { })をする上で何か例外が発生した場合は「エラー発生」としてログにその記録が残るようにしています。これはエラーが発生したら、開発者にすぐに知らせるような役割をしている、ということになります。
データベースを操作する
さて、データベースへの接続が確立できました。
次に見ていきたいのは、そのデータベースにある情報(レコード)を取得したり、追加したり、削除したりするための手順です。
今回は下記のように「小林由佳」という方のデータを挿入したい、ということを例に見ていきます。
まずは命令文を準備するープリペアードステートメントとプレースホルダ
「よし、すぐにデータを挿入しよう!」と思っても、そう簡単にはいかないのがデータベースくんです。
まずは挿入する前に、「こんなデータを今から入れたいです」という宣言をします。それがプリペアードステートメントと呼ばれる部分です。
プリペアードステートメント
プリペアード(準備された)+ステートメント(命令文)
データベースを利用する時には、追加・削除・取得といった同じような命令を何度も行う。それを一回一回処理するのは非常に手間。
なので予め変更する部分だけを虫食い状態にした命令文だけをデータベースに投げることで、後から値がきたら処理をするということにすれば繰り返しそれが使えるし、処理も高速になる。
プレースホルダ
簡単にいうと、上述した虫喰い部分のこと。
「?」を用いると疑問符プレースホルダ
「:名前」とすると名前つきのプレースホルダ *名前の部分は英字を用いる。
また、プレースホルダに値を割り当てることを「バインド」と言う。
また、プレースホルダは「エスケープ処理」も同時に行ってくれる。
エスケープ処理とは簡単にいうと「コードとして解釈されてしまう特殊な記号(シングルクォートなど)を、コードとして解釈されない文字に変換すること」を意味する。特にユーザーが入力するような部分(HTMLだとinputタグなど)には必ずこの処理が必要。
もしこれがされないままだと、入力する部分に悪意ある第三者がコードを書き、内部情報などを取得されてしまう危険性がある。
→参考:SQLインジェクション
話しが長くなってしまいましたが、実際のプリペアードステートメントは下記のように記述します。
$stmt = $dbh->prepare(‘INSERT INTO users (name, age, locate) VALUES (:name,:age,:locate)';
$stmt = プリペアードステートメントを入れる変数。
$dbh = PDOを使って確立したデータベースの情報などが入っている変数。
->prepare(‘SQL文’) =こうすることで「どんな操作を準備したいのか」を変数に入れることができる
*「SQL文ってなに?」という方はこちらをご覧ください。
本日のプログラミング学習 PHP / MYSQL 5hour PHP 鬼練1~4 2hour Aug / 36.5hour Total / 61.5hour 目次 1 SQ[…]
簡単にいうと、データベースを操作するための言語がSQLとなり、それを用いることでデータを削除したり、結合したり、検索したりできるというものになります。
値をプレースホルダにバインドするーbindValue
今度は作成したプリペアードステートメント・プレースホルダに値をセットしていきます。
基本の型は次のようになります。
bindValue(パラメータID,バインドする値,データ型)
パラメーターID → 疑問符プレースホルダの場合は「1」から、名前つきプレースホルダは「:名前」を記述。
データ型 → PHPの定義済み定数を記述し、バインドする値がどんな「型」かを記述する。デフォルトは【PDO::PARAM_STR】で文字列型となっている。
今回の例だと下記のようになります。
$name = '小林由佳';
$age = 24;
$locate = '群馬';
$stmt->bindValue(':name', $name);
$stmt->bindValue(':age', (int)$age, PDO::PARAM_INT);
$stme->bindValue(':locate', $locate);
二番目の「age」をバインドする際に、変数$ageの前に(int)としていますが、これは”キャスト”といって型を変換するためにこのようにしています。キャストについて詳しく見たい方はこちらをご参照下さい。
Webサービス部 Lesson14 / 1.5hour Sep / 10.5hour Total / 112hour 目次 1 phpは動的型付けをする言語2 phpの型変[…]
これでそれぞれのプレースホルダに値がセットされました!
用意された命令文を実行するーexecute
ここまで、長かったですね。
当初の目的である「小林由佳という方のデータを挿入したい」を実行するまで、非常に長い道のりでした。
あとは実行をするだけで上記の目的は達成されます。実行文は下記の通りです。
$stmt->execute();
はい!最後はあっさりです。
今まのデータベース接続から、データの挿入までをまとめると下記のようなコードになります。
try {
$dsn = 'mysql:dbname=php_sample01;host=localhost;charset=utf8';
$username = 'root';
$password = 'root';
$options = array(
PDO::ATTR_ERRMODE =>PDO::ERRMODE_SILENT,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
);
$dbh = new PDO($dsn, $username, $password, $options);
$stmt = $dbh->prepare(‘INSERT INTO users (name, age, locate) VALUES (:name,:age,:locate');
$name = '小林由佳';
$age = 24;
$locate = '群馬';
$stmt->bindValue(':name', $name);
$stmt->bindValue(':age', (int)$age, PDO::PARAM_INT);
$stmt->bindValue(':locate', $locate);
$stmt->execute();
} catch (Exception $e) {
error_log('エラー発生:' . $e->getMessage());
}
番外編ーvindValueのステップを省く
ここまで、データベース接続→プリペアードステートメント用意→値のバインド→実行という流れを見てきました。
正直、こう思いませんでしたか?
vindValue面倒くさい・・・・。
今回の例だとバインドする値は三つでした。(name/age/locate)
でもこれが10とかだとどうでしょう。かなり面倒くさいですね。
実はこのvindValueのステップ、なんと省略することができます。
省略するためにはexecuteの()の引数に配列を渡す。
先ほどの値のバインドは以下のようにしてました。
$name = '小林由佳';
$age = 24;
$locate = '群馬';
$stmt->bindValue(':name', $name);
$stmt->bindValue(':age', (int)$age, PDO::PARAM_INT);
$stmt->bindValue(':locate', $locate);
$stmt->execute();
これを省略するためには、下記のように記述します。
$name = '小林由佳';
$age = 24;
$locate = '群馬';
$stmt->execute(array(
':name' => $name,
':age' => $age,
':locate' => $locate));
配列形式で入れればいいので、[‘:name’ => $name]の形でも可能です。
値のバインドと実行をひとまとめにできる、という感じですね。
注意点
① 値はnull値以外全て「PDO::PARAM_STR」となる。
② bindValueで既に値がバインドされていた場合でも、それらは全て無視されてしまう。
データベースが操作できると楽しい
僕がPHPで最初につまづいたのが、このデータベースの接続と操作です。
最初にコードを見たときは「何これ!?全然意味わからない!」となり、延々とググってはコードを見返してを繰り返していました。
しばらくそんなことを繰り返して、少しながらも理解して実際に自分の狙い通りにデータベースの操作ができると、すごく達成感がありました。
初めはとっつきにくいPHPのデータベース。
その理解に少しでもこの記事が役に立ってくれると嬉しいです。
参考サイト