PHPオブジェクト指向実践
メッセージボード
クラスの設計
メッセージボードを作ってみます。
今回作るメッセージボードは以下のような仕様とします。
- 掲示板のような形式
- 1行メッセージボードとし、複数行の書き込み不可
- 返信機能なし
- データはCSV形式(カンマ区切りテキスト)でテキストファイルに保存する
以上を踏まえてまずはクラスを設計していきます。
<?php class MessageBoard { protected $dataFilePath; // データファイルのパス設定 public function setDataFilePath() { } // メッセージデータ取得 public function getMessagesData() { } // 書き込み処理 public function write() { } } ?>
必要な処理は、
- 現在書き込まれているメッセージの表示用のデータ取得
- 新たなメッセージの保存
- メッセージを保存するテキストファイルの指定
というところです。
簡易カウンターのところではデータ保存ファイルを定数で設定していましたが、
今回は変数として、クラス外部より保存ファイルを自由に設定できるようにします。
これは、このメッセージボードクラスを汎用的に使用できるようにしたい為です。
例えばサイト上で話題ごとにメッセージボードを分けて複数作りたいような場合、
保存ファイルを変えることで、同じクラスを別々のメッセージボードに使いまわすことが出来ます。
ということでメソッドの中身を実装していきます。
データファイルのパス設定メソッドの実装
まずメッセージデータを保存するテキストファイルのパスを設定するメソッドです。
// データファイルのパス設定
public function setDataFilePath($path)
{
if (false == file_exists($path)) {
if (false == @touch($path)) {
throw new @CException@(\"エラーが発生しました\");
}
}
$this->dataFilePath = $path;
}
このメソッドでやっていることは、
引数で指定されたパスをクラスのフィールドに設定していることです。
設定する前に、そのファイルが実際に存在するかチェックをしています。
もし存在しなければ、ファイルを新規作成しています。
以後、データ取得や保存などの各メソッドでは、
ここで設定したパスのファイルを使用します。
メッセージデータ取得メソッドの実装
次に書き込みデータを保存しているテキストファイルよりデータを取得するメソッドを見ていきます。
// メッセージデータ取得
public function getMessageData()
{
// ファイルよりCSVデータ取得
$fp = fopen($this->dataFilePath, \'r\');
$data = array();
while ($row= fgetcsv($fp)) {
$data[] = $row;
}
fclose($fp);
// 配列を逆転
$data = array_reverse($data);
return $data;
}
メッセージデータはテキストファイルにCSV形式で保存されているため、
これを読み込み、2次元配列として取得します。
そしてメッセージデータは書き込みがあるたびに下へ下へ追加されて行っているので、
言ってみればそのまま取得すると日付の古い順に並びます。
なのでデータ取得後、配列の順序を逆転することで日付の新しい順に並べ替えています。
書き込み処理メソッドの実装
次に書き込み処理メソッドです。
// 書き込み処理
public function write($post)
{
// 新規番号取得
$no = $this->getNewNo();
// カンマを削除
$name = str_replace(",", "", $post['name']);
$message = str_replace(",", "", $post['message']);
// 番号、投稿者名、メッセージをカンマ区切りで文字列結合
$lineData = sprintf("%s,%s,%s¥r¥n", $no, $name, $message);
// ファイルに書き込む
$fp = fopen($this->dataFilePath, 'a');
fputs($fp, $lineData . '¥r¥n');
fclose($fp);
}
// 新規番号取得
protected function getNewNo()
{
$data = $this->getMessageData();
$no = 1;
if (0 < count($data)) {
$no = $data[0][0];
$no++;
}
return $no;
}
書き込みは、投稿者がサイト上のフォームの書き込みボタンをクリックすることで送信されるPOSTデータを基に処理を行います。
書き込み処理メソッドには引数として$_POSTをそのまま渡すことを想定します。
その上でまずPOSTデータをカンマ区切りで文字列結合し、データ保存ファイルに書き込むためのCSV形式の行データを作成します。
またメッセージデータは書き込みNoとして連番項目を持ちますので、
現在の最大の番号に+1した数値を書き込み用に取得する必要があります。
今回はこの処理を別メソッドにしました。これはクラス外部より直接実行できる必要はなないのでprotectedです。
そして書き込み番号とPOSTデータを基に書き込み用のCSVデータを作り、これをファイルの末尾に追加書き込みします。
保存データがカンマ区切り形式の為、書き込み内容にカンマが含まれるとおかしなことになります。なので、カンマは強制的に削除しています。
以上、全てのメソッドが実装された状態のクラスを見てみましょう。
<?php class MessageBoard { protected $dataFilePath; // データファイルのパス設定 public function setDataFilePath($path) { if (false == file_exists($path)) { if (false == @touch($path)) { throw new Exception("エラーが発生しました"); } } $this->dataFilePath = $path; } // メッセージデータ取得 public function getMessageData() { // ファイルよりCSVデータ取得 $fp = fopen($this->dataFilePath, 'r'); $data = array(); while ($row= fgetcsv($fp)) { $data[] = $row; } fclose($fp); // 配列を逆転 $data = array_reverse($data); return $data; } // 書き込み処理 public function write($post) { // 新規番号取得@ $no = $this->getNewNo(); // カンマを削除 $name = str_replace(',', '', $post['name']); $message = str_replace(',', '', $post['message']); // 番号、投稿者名、メッセージをカンマ区切りで文字列結合 $lineData = sprintf("%s,%s,%s\r\n", $no, $name, $message); // ファイルに書き込む $fp = fopen($this->dataFilePath, 'a'); fputs($fp, $lineData); fclose($fp); } // 新規番号取得 protected function getNewNo() { $data = $this->getMessageData(); $no = 1; if (0 < count($data)) { $no = $data[0][0]; $no++; } return $no; } } ?>
メインスクリプトの実装
<?php
require_once '../class/message_board/MessageBoard.php';
$mb = new @CMessageBoard@();
// データ保存ファイル設定
$datFile = '../data/message_board.dat';
$mb->setDataFilePath($datFile);
// 書き込みボタンが押された場合
if (true == isset($_POST['write'])) {
// 書き込み処理
$mb->write($_POST);
// 更新ボタン等による2重処理防止の為リダイレクト
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
// メッセージデータ取得
$data= $mb->getMessageData();
?>
<html>
<head>
<style type="text/css">
input.msg {
width:500px;
}
td {
border:1px solid #555555;
}
</style>
<title>メッセージボード</title>
</head>
<body>
<form method="post" action="#">
名前:<input type="text" name="name" /><br />
メッセージ:<input type="text" name="message" class="msg" /><br />
<input type="submit" name="write" value="書き込む" />
</form>
<table style="border:1px;">
<?php
// メッセージデータをもとに表のHTML生成
foreach ($data as $row) {
echo '<tr>';
echo sprintf('<td>%s</td>', $row[0]);
echo sprintf('<td>%s</td>', $row[1]);
echo sprintf('<td>%s</td>', $row[2]);
echo '</tr>';
}
?>
</table>
</body>
</html>
メインスクリプトです。
MessageBoardクラスのインスタンスを生成した後、
まずメッセージデータ保存ファイルのパスを設定します。
もし「書き込み」ボタンがクリックされてきた場合、
$_POST['write']が存在するので、この場合は書き込みメソッドを実行します。
引数として$_POSTをそのまま渡しています。
処理後、自URLにリダイレクトします。
書き込み処理後、そのまま処理を続けてページ表示を行った場合、
もしブラウザの「更新」ボタンが押されたら、もう一度書き込み処理が実行されてしまうためです。
$_POSTデータが送信されてきた状態で「更新」が押されると、$_POST情報も残っているためです。
最後にメッセージデータ取得メソッドの実行し、メッセージデータを配列で取得します。
この配列をforeachでループしながら、tableタグの内容を生成していくわけです。
というわけで今回作成したファイル群の階層図です。
public_html/ ∟class/ ∟message_board/ ∟MessageBoard.php ∟data/ ∟message_board.dat ∟message_board.php
再利用
冒頭でも触れたとおり、今回はデータ保存ファイルのパスを設定できるようにしまし、汎用性を持たせました。
基本的にクラスは固定値的な要素は無くし、出来る限り汎用的に設計すべきです。
例えば今回のMessageBoardクラスの場合、保存ファイルを変えることにより、複数のメッセージボードを設置することが可能です。
ファイル構成的には例えば以下のような感じになります。
public_html/ ∟class/ ∟message_board/ ∟MessageBoard.php ∟data/ ∟message_board.dat ∟impression_board.dat ∟message_board.php ∟impression_board.php
ホームページを見てもらった感想でも書き込んでもらおうと、
impression_board.phpを追加しました。
impression_board.phpの中身はmessage_board.phpと全く同じで、
そのままコピーして名前を変えるだけです。
そして唯一、設定するデータ保存ファイルのパスのみ変えます。
ファイルはimpression_board.datを指定します。
これにより、同じMessageBoardクラスを使用して処理をしていても、
参照しているデータは別のものなので、別々のメッセージボードとして機能します。
こういうところはクラスのメリットといえます。
基礎編で、クラスのインスタンスの特性として、フィールドという形で独自にデータを保持することが出来るということを言っていますが、
まさにそれを生かす例です。

カウンター

