logo

パレット - カラフルに🎨

Palette — ビジュアルページビルダー、デザインの専門知識は不要です。

ライブデモ パレットをダウンロード

Scroll

PHP レッスン — レッスン 3.5 — MySQL DB の操作。JOIN 演算子。サーバーへのファイルアップロード。

08/12/2019, by Ivan

このレッスンを書き始める前に、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 ダンプを追加していきます。