明らかにSQLやってるのでカテゴリ追加しました。データベース。
今日はちゃんとPHPもするよ!
PHPからデータベースに接続する
PDOを使います。
1 2 3 4 5 6 |
<?php try { $db = new PDO('mysql:host=localhost;dbname=lesson_db;charset=utf8', 'user', 'pass'); } catch(PDOException $e) { exit('接続失敗:'.$e->getMessage()); } |
PDO::__construct
データベースへの接続を表す PDO インスタンスを生成する。
第一引数:データベース接続情報
mysqlを利用しているのでmysql:から始まり、以下ホスト名、データベース名、文字コードを入力している。
utf8はutf-8と書くとエラーになるので注意。
第二引数:ユーザー名
第三引数:パスワード
マニュアルの例よるとデータベースに接続できなかった時にPDOExceptionを投げるらしいので、例外処理をしておく。
例外処理についてはJavaと文法に差が無いので割愛するが、気になる方はマニュアルを見てみるとよいかも。
PHPからレコードを登録する
コード
フォーム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>レコード登録</title> </head> <body> <h1>登録フォーム</h1> <form action="record.php" method="POST"> <div> <label for="title">名前:</label><br /> <input type="text" name="name" size="50" maxlength="255"> </div> <input type="submit" value="登録"> </form> </body> </html> |
登録処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php $dsn = 'mysql:host=localhost;dbname=lesson_db;charset=utf8'; $user = 'user'; $password = 'pass'; try { $db = new PDO($dsn, $user, $password); $stt = $db->prepare('INSERT INTO nametable(name) VALUES(:name)'); $stt->bindValue(':name', $_POST['name']); $stt->execute(); $db = NULL; } catch(PDOException $e) { exit('接続失敗:'.$e->getMessage()); } header('Location: http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/form.php'); |
PDO::prepare
文を実行する準備を行い、文オブジェクトを返す。
記法は
PDOオブジェクト->prepare(SQL文,ドライバオプション);
だが第二引数のドライバオプションは「通常、スクロール可能なカーソルを要求するために PDO::ATTR_CURSOR に PDO::CURSOR_SCROLL を設定する場合に使用することになるでしょう。 」ということらしいので今回は関係ない。
使うタイミングが分からない・・・・なんだろこれ・・・
PDOStatement::bindValue
値をパラメータにバインド(結合)する。
第一引数:パラメータID。パラメータ名は「:名前」という形式になっている。
第二引数:バインドする値。今回はフォームから得た値を入れている。
第三引数:データ型。省略可。明示する必要があれば使う。
PDOStatement::execute
プリペアドステートメントを実行する。
prepareで指定したSQLを実行する。
PHPからテーブルを作成する
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // データベース接続を外部化した(GETDBメソッドを定義、PDOオブジェクトを返す) require_once '../php_include/db_connect.php'; $db = getDB(); $db -> query('SET NAMES utf8'); // データベースを定義する $create_query = <<<___SQL___ CREATE TABLE IF NOT EXISTS words( word_id INT PRIMARY KEY AUTO_INCREMENT, title TEXT, body TEXT ); ___SQL___; $db->exec($create_query); |
CREATE TABLE
テーブルを作る
EXISTS(NOT EXISTS)
EXISTS以下のもの(サブクエリ)が存在するものならばTRUE。なければFALSE。
NOTをつけると否定になるので存在しなければTRUE。あればFALSE。
今回はIF NOT EXISTS wordsなので、「もし wordsが 存在 しなければ テーブルwordsを作る」ということになる。
これを付けないで実行するとすでに同名テーブルが存在する場合エラーになる。
ただし、PHPからはエラーについては何も表示されないし、上書きされる訳でも無い。
フィールドについて
テーブルのフィールドとしている「word_id INT PRIMARY KEY AUTO_INCREMENT」について少し解説。
「フィールド名 データ型 インデックス(PRIMARYは主キー) 自動連番」のフィールドを作るように指示している。
exec
外部プログラムを実行する。
PHPからテーブルを削除する
テーブル作成時のヒアドキュメント___SQL___の中身を
DROP TABLE IF EXISTS words;
とする。
「もし wordsが 存在するなら テーブルwordsを削除する」
CREATE TABLEと同様にIF EXISTSを付けないで実行すると指定したテーブルが存在しない場合にエラーになる。
DROP TABLE
テーブルを削除する。
レコードも全部削除されるので仕様の際は要注意。
大量のレコードを登録する
46725行のタブ区切りデータを渡されたので一括登録。
データが大量なのでトランザクション機能で高速且つ一括で登録する。高速化するのでタイムアウトしにくくなる。
途中で処理が中断された場合は1レコードたりとも登録しない。
ちなみにストレージエンジンがInnoDBになっていないと使えない。
phpMyAdminで確認する場合は、テーブル選択 > 操作 > テーブルオプション > ストレージエンジン
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php require_once '../php_include/db_connect.php'; $db = getDB(); $db -> query('SET NAMES utf8'); try { $create_query = <<<___SQL___ CREATE TABLE IF NOT EXISTS words( word_id INT PRIMARY KEY AUTO_INCREMENT, title TEXT, body TEXT ); ___SQL___; $db->exec($create_query); $stt = $db->prepare('INSERT INTO words(title, body) VALUES(:title, :body)'); $db->begintransaction(); $file = @fopen('largedata.txt', 'rb') or exit('ファイルが開けませんでした'); flock($file, LOCK_SH); while ($row = fgetcsv($file, 0, "\t")){ $stt->bindValue(':title', $row[0]); $stt->bindValue(':body', $row[1]); $stt->execute(); echo $row[0].':'.$row[1]."\n"; } flock($file, LOCK_UN); fclose($file); $db->commit(); } catch(PDOException $e) { $db->rollBack(); //途中でミスがあったらロールバック(何もしない) exit('エラーメッセージ:'.$e->getMessage()); } |
先生のコードの繰り返し処理に一部まだ使ってない関数があったので一部抜粋(変数は自分の作ったものに合わせた)
20 21 22 23 24 25 26 27 |
while ($line = fgets($file) ) { list($title,$body) = explode("\t", $line, 2); //list()は配列要素を変数に格納する if ($title == ""){ continue; }// 空なら代入しない $stt->bindValue(':title',$title); $stt->bindValue(':body',$body); $stt->execute(); // データベースに挿入 echo $title."\n"; // 経過報告を出力 } |
PDO::beginTransaction
トランザクションを開始する。
自動的にコミットしなくなる。commitを宣言するまで書き込み処理を行わなくなる。
コミットについてMySQLってわけじゃないけど分かりやすかったのでAdobeのコレ置いておきます。
PDO::commit
トランザクションをコミットする。
PDO::rollBack
トランザクションをロールバックする。
PDOExceptionがスローされた時にロールバックするようにしている。
コミットかロールバックの処理がなされるとオートコミットがオンになる。
list
配列と同様の形式で、複数の変数への代入を行う。
今回はfgetsでとった1行をexplodeでぶったぎって$titleと$bodyに代入している。
$titleが空だったらcontinueでデータベース挿入処理をスキップしている。
Javaと同じなのでcontinueの説明は割愛。
テーブルを一覧表示する
とりあえず下記SQL文でテーブルを作成
1 2 3 4 5 6 7 |
CREATE TABLE IF NOT EXISTS schedule( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL DEFAULT '無題', date DATE, time TIME, memo TEXT NULL ); |
で10個くらいレコードを登録(面倒なのでforで回して同じ中身を入れた)
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php require_once '../php_include/Encode.php'; require_once '../php_include/db_connect.php'; $db = getDB(); function format($datetime, $format) { $ts = strtotime($datetime); print(date($format, $ts)); } $stt = $db->prepare('SELECT * FROM schedule ORDER BY date, time'); $stt->execute(); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>一覧</title> </head> <body> <h1>スケジュール一覧</h1> <table border="solid 1px #FFFFFF"> <tr> <th>日付</th> <th>時刻</th> <th>予定名</th> <th>備考</th> </tr> <?php while ($row = $stt->fetch()): ?> <tr> <td><?php format($row['date'], 'Y/m/d'); ?></td> <td><?php format($row['time'], 'H:i'); ?></td> <td><?php print(e($row['title'])); ?></td> <td><?php print(e($row['memo'])); ?></td> </tr> <?php endwhile; ?> </table> </body> </html> |
strtotime
英文形式の日付を Unix タイムスタンプに変換する。
今回は2014-02-14の形でフィールドに入っているので一度変換しておく。
それをdateで書式を整形する。
PDOStatement::fetch
結果セットから次の行を取得する。
scheduleテーブルからレコードを一行ずつ行が無くなるまでループさせて取得している。
結果表示
今日のひとこと
Hello worldしか出力したことないッスよ
コメント
No Trackbacks.