PHP レッスン — レッスン 3.5 — MySQL DB の操作。JOIN 演算子。サーバーへのファイルアップロード。
このレッスンを書き始める前に、JOIN 演算子を使ったクエリをどう教えるのが良いか長く考えました。というのも、JOIN 演算子は複数のテーブルから同時に抽出するために必要だからです。もう 1 つテーブルが必要なので、それを作りましょう。このレッスンでフォームを通じてアップロードするファイル用のテーブルを作ることを提案します。DB の操作とフォームの操作という 2 つの異なる方向のレッスンになります。
では、ファイルアップロード用のフィールドの追加から始めましょう。フォームがファイルをアップロードできるようにするには、そのパラメーターにフォームの種類を追加する必要があります:
$content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">';
こうして、enctype パラメーターを通じて、このフォームでファイルをアップロードすることをブラウザに知らせます。フォームの準備ができたので、ファイルアップロードのフィールドを追加しましょう:
public function display_admin() { // メッセージ入力のメソッド
$content = '';
$content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">';
$content .= '<label for="title">名前:</label><br />';
$content .= '<input name="title" id="title" type="text" maxlength="150" />';
$content .= '<div class="clear"></div>';
$content .= '<label for="bodytext">メッセージ:</label><br />';
$content .= '<textarea name="bodytext" id="bodytext"></textarea>';
$content .= '<input type="file" name="filename">'; // ファイルアップロードフィールド
$content .= '<div class="clear"></div>';
$content .= '<input type="submit" value="メッセージを追加" />';
$content .= '</form>';
$content .= '<p><a href="/index.php">トップに戻る</a></p>';
return $content;
}
type が file の input を通じて、ファイルをアップロードします。ファイルを保存してメッセージ追加ページを更新すると、ファイルのアップロードフィールドが表示されるはずです。
では、POST リクエストでファイルがどのように渡されるか見てみましょう。
public function write($p) {
print_r($p); // フォームの配列を出力する
print_r($_FILES); // ファイルの配列を出力する
$sql = 'INSERT INTO Messages (title, bodytext, created) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ')';
return mysql_query($sql);
}
では、何かファイルをアップロードに設定して、メッセージを保存しましょう。フォームを通じてアップロードされるすべてのファイルは、スーパーグローバル変数 $_FILES で利用できます。ファイルをアップロードして、次のような配列が得られました:
Array (
[title] => asfasdf
[bodytext] => asfasdf
)
Array (
[filename] => Array (
[name] => ip.txt
[type] => text/plain
[tmp_name] => Y:\tmp\phpAA.tmp
[error] => 0
[size] => 13
)
)
データとファイルの配列ができたので、アップロードされたファイルを保存するためのファイルテーブルを作りましょう。まず、サイトのルートに files フォルダを作ります。OS が linux 系の場合、あるいはホスティング上で作業している場合は、私たちのスクリプトがこのフォルダにファイルを保存できるよう、このフォルダにアクセス権 777 を付けてください。
buildDB() メソッドのテーブル作成クエリを変更します:
$sql = "CREATE TABLE Messages ( mid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(mid), title varchar(15), bodytext text, created int(11), file int(11), ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; CREATE TABLE Files ( fid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(fid), filename varchar(255), filepath varchar(255), filemime varchar(255), filesize int(10), timestamp int(10), )ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;";
新しいテーブルの追加に加えて、ファイルテーブルの外部キーとなる fid フィールドも追加します。新しいファイルテーブルには、次のフィールドがあります:
fid — 私たちのテーブルの主キー、ファイルの番号です。
filename — ファイル名です。
filepath — サイトのルートからのファイルへのパスです。
filemime — ファイルの拡張子や用途を判定するための、ファイルの種類です(例えばテキストファイルなら text/plain、画像なら image/jpg、image/png など)。
filesize — バイト単位のファイルサイズです。
timestamp — ファイルのアップロード時刻です。
では、ファイルを保存し、DB にファイルを追加するクエリを書きましょう。必要なフィールドは phpMyAdmin で作成できます。
if($_FILES["filename"]["size"] > 1024*3*1024)
{
echo ("ファイルサイズが 3 メガバイトを超えています");
exit;
}
// ファイルがアップロードされたか確認する
if(is_uploaded_file($_FILES["filename"]["tmp_name"]))
{
// ファイルが正常にアップロードされたら、
// 一時ディレクトリから最終ディレクトリへ移動する
move_uploaded_file($_FILES["filename"]["tmp_name"], "/files/".$_FILES["filename"]["name"]);
} else {
echo("ファイルアップロードのエラー");
}
こうしてファイルを files フォルダに保存します。ファイルのアップロードが成功したら、Files テーブルへの追加クエリを DB に挿入します:
$sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp)
VALUES ("'. $_FILES['filename']['name'] . '",
"files/' . $_FILES['filename']['name'] . '",
"'. $_FILES['filename']['type'] .'",
'. $_FILES['filename']['size'] .',
'. time() . ')';
mysql_query($sql);
DB にファイルのレコードを挿入したので、メッセージも挿入する必要があります。メッセージ挿入のクエリも少し変更し、そこに fid フィールドを追加します。
$sql = 'INSERT INTO Messages (title, bodytext, created, fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID())';
last_insert_id() 関数に注目してください。これは mysql の関数で、DB のいずれかのテーブルに挿入された最後の ID を出力します。私たちの場合、最後のクエリはファイルテーブルにレコードを挿入したので、ID もファイルテーブルから出力されます。
これでレコードがテーブルに挿入されるので、あとは JOIN 演算子を使ってそれらを出力するだけです。ファイル名も出力するよう、メッセージ出力のクエリを変更しましょう:
public function display_public() { // メッセージ出力のメソッド
$content = '';
$sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid ORDER BY mid DESC';
$result = mysql_query($sql) or die(mysql_error());
...
この場合、JOIN 演算子を LEFT と一緒に使いました。抽出のためのテーブルの結合はさまざまな方法で行え、次のレッスンの 1 つで JOIN の使用をより詳しく取り上げます。ひとまず、LEFT JOIN は fid フィールドがないものも含めてすべてのレコードを出力する、と約束しておきましょう。
では、クエリの配列を出力すると:
public function display_public() { // メッセージ出力のメソッド
$content = '';
$sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid ORDER BY mid DESC';
$result = mysql_query($sql) or die(mysql_error());
while($row = mysql_fetch_array($result)){ // 抽出クエリの変数は、特別な関数 mysql_fetch_array() で処理する必要がある
print_r($row);
すると、メッセージテーブルからもファイルテーブルからもデータが出力されるのが分かります。しかも、対応するファイルがなければ、フィールドは空で出力されます。ですから、これらのフィールドに何かあるか確認する必要があります。
if(!empty($row['filename'])){
$content .= '<p>添付: <a target="_blank" href="/'. $row['filepath'] .'">'. $row['filename'] .'</a></p>';
}
これで、メッセージと一緒に、アップロードしたファイルへのリンクとして添付も出力されます。次のレッスンでは、複数のテーブルから同時にデータを出力できる素晴らしい JOIN 演算子を、より詳しく取り上げます。添付には、これから phpMyAdmin で作成した最新の DB ダンプを追加していきます。