PHPプログラミング

最終更新日:
公開日:

ワークショップ

ひと言掲示板を作る(5)

ファイルからデータを読み込む

「ひと言掲示板」について、投稿されたメッセージが保存されたファイルからデータを読み込んで表示する機能を付けます。

この記事のポイント

  • メッセージデータをファイルから1行ずつ読み込む
  • PHPとHTMLが共存しても分かりやすいコードを書く

目次

メッセージのデータをファイルから取得する

前回までで掲示板にメッセージを書き込む機能を作ってきましたが、今回はファイルに保存されたメッセージのデータを読み込む機能を付けていきます。

誰かが書き込んだメッセージを読むことができるようになり、やっと掲示板らしくなってきます。

今回作成するメッセージ表示エリア

「ひと言掲示板を作る」の概要については「ひと言掲示板を作る」をご覧ください。
デモはこちら

前回までに作成したコードはこちら:Github

ファイルを読み込む

前回、fopen関数でファイルを開いて、fwrite関数でデータをファイルに書き込んでfclose関数で安全に閉じる操作を行いました。
ファイルの読み込みについても、fwrite関数がデータを読み込むfpus関数に置き換わるだけで基本的な流れは同様です。

今回は仮に、「message.txt」には次のようなメッセージのデータが入っているとします。

message.txtのデータ例

'Emily','掲示板に書き込んでみる','2019-03-20 23:22:47'
'太郎','おはようございます!','2019-03-20 23:25:27'
'健二郎','今日は春のような暖かさでしたね。そろそろダウンは必要ないかも。','2019-03-20 23:26:04'
'りょうこ','花粉が...','2019-03-22 11:26:19'

まずは前回と同様にファイルを開く&閉じる操作を記述していきましょう。

index.php

<?php

// メッセージを保存するファイルのパス設定
define( 'FILENAME', './message.txt');

// タイムゾーン設定
date_default_timezone_set('Asia/Tokyo');

// 変数の初期化
$current_date = null;
$data = null;
$file_handle = null;

if( !empty($_POST['btn_submit']) ) {
	
	if( $file_handle = fopen( FILENAME, "a") ) {

	    // 書き込み日時を取得
		$current_date = date("Y-m-d H:i:s");

		// 書き込むデータを作成
		$data = "'".$_POST['view_name']."','".$_POST['message']."','".$current_date."'\n";
	
		// 書き込み
		fwrite( $file_handle, $data);
	
		// ファイルを閉じる
		fclose( $file_handle);
	}		
}


if( $file_handle = fopen( FILENAME,'r') ) {

    // ファイルを閉じる
    fclose( $file_handle);
}

?>

---- 以下省略 ----

今回はファイルを開くコードの他に、上の方で「// 変数の初期化」を追加しました。

変数の初期化とは、変数をあらかじめ「null」など空の値で宣言しておくことで存在しない変数を参照するエラーを防いだり、型をあらかじめ設定しておくことで意図しない動作を防ぐことができます。
また、複数のエンジニア間でコードを共有する際にも使用する変数を共有し、コード全体の動きや意図をトレースしやすくする役割を持ちます。

PHPでは変数の型を動的に変更する機能があるため必須ではありませんが、出来るだけ宣言しておくと後々メンテナンスが楽になるため強くオススメしたい慣習です。

もう1つ下の方に追加したif文については、ファイルを開く&閉じる操作です。
ここではファイルの読み込みを行いたいため、fopen関数の2つ目のパラメータに読み込みモードである「r」を指定します。

ファイルからデータを1行ずつ取得する

続いて、開いたファイルからデータを読み込んでいきましょう。
先ほど追加したif文の中に、次のようなwhile文を追記してください。

index.php

<?php

// メッセージを保存するファイルのパス設定
define( 'FILENAME', './message.txt');

// タイムゾーン設定
date_default_timezone_set('Asia/Tokyo');

// 変数の初期化
$current_date = null;
$data = null;
$file_handle = null;

---- 省略 ----

if( $file_handle = fopen( FILENAME,'r') ) {
    while( $data = fgets($file_handle) ){
        echo $data . "<br>";
    }

    // ファイルを閉じる
    fclose( $file_handle);
}

?>

---- 以下省略 ----

上記コードを実行すると、「message.txt」のデータが読み込まれて次のように表示されます。

読み込んだデータが表示される例

while文の条件式の中でfgets関数を呼び出しています。
こちらの関数はファイルから1行ずつデータを取得するための関数になります。

引数で渡す「ファイルポインターリソース」から、データを読み込むファイルと位置を特定しています。
fgets関数を1度実行して1行読み込むと、このファイルポインターリソースの位置も都度更新されていくので、ファイルが終わるまで1行ずつデータを読み込むことができる仕組みです。

読み込んだデータを配列に格納する

ファイルにあるメッセージのデータを1行ずつ読み込むことができたので、次には表示する前準備としてデータを配列に格納していきます。
次のコードを追記してください。

index.php

<?php

// メッセージを保存するファイルのパス設定
define( 'FILENAME', './message.txt');

// タイムゾーン設定
date_default_timezone_set('Asia/Tokyo');

// 変数の初期化
$current_date = null;
$data = null;
$file_handle = null;
$split_data = null;
$message = array();
$message_array = array();

---- 省略 ----

if( $file_handle = fopen( FILENAME,'r') ) {
    while( $data = fgets($file_handle) ){

        $split_data = preg_split( '/\'/', $data);

        $message = array(
            'view_name' => $split_data[1],
            'message' => $split_data[3],
            'post_date' => $split_data[5]
        );
        array_unshift( $message_array, $message);
    }

    // ファイルを閉じる
    fclose( $file_handle);
}

?>

---- 以下省略 ----

preg_split関数は文字列を特定の文字で分割する関数です。
正規表現を使って、今回は「'」で分割しています。
前についている「\ (バックスラッシュ)」はエスケープするための記号で、通常は記号として扱われてしまう「'」を文字として検索するために必要な記号です。

ここで、読み込まれたデータは次のような配列に分割されます。

分割されたデータ

array(7) {
	[0]=> string(0) ""
	[1]=> string(5) "Emily"
	[2]=> string(1) ","
	[3]=> string(33) "掲示板に書き込んでみる"
	[4]=> string(1) ","
	[5]=> string(19) "2019-03-20 23:22:47"
	[6]=> string(1) " "
}

表示名が[1]、メッセージは[3]、投稿日時は[5]からアクセスできることが分かりました。
そこで、一旦$messageに連想配列の形で取得したデータを格納します。

最後に$message_array$messageごと格納します。
この操作を投稿されたメッセージの数だけ繰り返すと、$message_arrayに全てのメッセージのデータが入るという流れです。

HTMLにメッセージを出力する

ここまでに取得したメッセージのデータをHTMLに出力していきます。
HTMLにあった「<!– ここに投稿されたメッセージを表示 –>」の部分に、投稿されたメッセージを表示するコードを追記しましょう。

index.php

---- 省略 ----

<body>
<h1>ひと言掲示板</h1>
<form method="post">
	<div>
		<label for="view_name">表示名</label>
		<input id="view_name" type="text" name="view_name" value="">
	</div>
	<div>
		<label for="message">ひと言メッセージ</label>
		<textarea id="message" name="message"></textarea>
	</div>
	<input type="submit" name="btn_submit" value="書き込む">
</form>
<hr>
<section>
<?php if( !empty($message_array) ): ?>
<?php foreach( $message_array as $value ): ?>
<article>
	<div class="info">
		<h2><?php echo $value['view_name']; ?></h2>
		<time><?php echo date('Y年m月d日 H:i', strtotime($value['post_date'])); ?></time>
	</div>
	<p><?php echo $value['message']; ?></p>
</article>
<?php endforeach; ?>
<?php endif; ?>
</section>
</body>
</html>

一番外側のif文$message_arrayが空でないかをチェックして、そもそも表示するメッセージがあるかを確認しています。
ここで表示するメッセージがあることが分かると、その中のforeach文でメッセージの件数分だけループを行います。

if文foreach文の「{」と「}」書き方が通常とは少し異なっていることに気づいたかもしれません。
これは書き方のバリエーションの1種で、次のようにいつも通りの形で書いてももちろん正常に動作します。

いつもの書き方

<?php
if( !empty($message_array) ){
	foreach( $message_array as $value ){
		///////////////
		///////////////
		///////////////
	}
}	
?>

ではどうして今回のように「if( // ):「」と「endif;」の形で書くのかというと、PHPコードの途中でHTMLコードが混じっているときにif文がどこで閉じているかを分かりやすくするためです。

どのようなコードの書き方が分かりやすいかは個人の裁量によりますので、実際には書き方に正解はありません。
1つの書き方として覚えておいてください。

foreach文$message_arrayからメッセージ1件分のデータを取り出し、$valueに入れました。
続いてHTMLと一緒にデータを出力していきます。

index.php

<article>
	<div class="info">
		<h2><?php echo $value['view_name']; ?></h2>
		<time><?php echo date('Y年m月d日 H:i', strtotime($value['post_date'])); ?></time>
	</div>
	<p><?php echo nl2br($value['message']); ?></p>
</article>

表示名、投稿日時、メッセージ内容の3つをそれぞれecho関数で出力しています。

投稿日時のみやや複雑で、まず文字列形式になっている時間をstrtotime関数でタイムスタンプ形式に変換します。
その後、date関数で時刻フォーマット「'Y年m月d日 H:i'」の形で時刻を取得し、出力しています。

ここまでで、一度ブラウザを更新などして実行してみてください。
今までに投稿したメッセージが表示されたら成功です。

メッセージが表示されている例

以上で、掲示板への書き込みと閲覧ができるようになりました。
掲示板として最低限の機能が揃い、形になってきましたね。
次回からは、掲示板をより便利にする機能を少しずつ加えていきます。

今回作成したコード:Github

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

コメントありがとうございます!
運営の参考にさせていただきます。