あーる学習帳

自分が勉強したことや気になることなど、99%自分用です。コードを書いてるのでPCから閲覧を推奨。

PHP実習:ひとこと掲示板を作ろう

昨夜はストレスの赴くまま、久しぶりに引っ張り出した「Horizon Zero Dawn」をずっとやっておりました…。敵を誘い出して罠にはめていくの楽しい…。

 

今日は第11章のまとめとしてひとこと掲示板を作る。その前に…

 

第11章-04

まとめの前準備として、関数のみを分離したファイルを作成しておく。

ファイル名は「functions.php」とし、以下のコードを書いていく。

 <?php
 function html_escape($word){
  //クライアントからフォームへの入力値を出力する場合、必ず行う処理
  return htmlspecialchars($word,ENT_QUOTES,'UTF-8');
 }

function get_post($key){
 //POSTデータ取得用
 if (isset($_POST[$key])) {
  $var=trim($_POST[$key]);
  return $var;
 }
}

function check_words($word,$length){
 //空文字 or 文字数が制限よりも多かった場合はFalseを返す
 if(mb_strlen($word)===0){
  return FALSE;
 } elseif(mb_strlen($word)>$length){
  return FALSE;
 } else{
  return TRUE;
 }
}

function get_db_connect(){
 // MySQL接続用関数
 try{
  $dsn='mysql:dbname=board;host=localhost;charset=utf8'
  $user='root';
  $password='';
  $dbh=new PDO($dsn,$user,$password);
 } catch(PDOExeption $e){
  echo ($e->getMessage());
  die();
 }
 $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
 return $dbh;
}

 これを作って使うファイルで読み込みを行えば、データベースの接続も「$dbh=get_db_connect();」とだけ書けば済むようになる。

 

第11章ー05

いよいよひとこと掲示板を作る実習に入る。「名前」「ひとこと」「日時」が書き込めるようにし、日時については自動取得する。制作の流れは以下の通り。

  1. ひとことを登録するテーブル(board)を作る
  2. 関数ファイルを読み込み、HTMLファイルも別に分ける
  3. フォームにはバリデーションをつけ、文字数に問題があった場合はエラーを出す

要件定義は以下の通り。

  • 名前欄は50文字まで、コメント欄は200字まで、どちらも入力必須
  • フォームからのデータ挿入時にはbindValue()を使う
  • 入力データを出力するときは、先ほど作ったhtml_escape()を使う

まず、MySQLでテーブルを作成する。

f:id:R_de_aru:20180707181715p:plain

だいぶ手順を忘れているので、今回はデータベースから分けることにした。

MySQL起動

mysql -u root

データベース作成

create database board;

ユーザー設定

grant all on board.* to vagrant@localhost identified by 'abc123';

データベースへ入る

use board;

テーブル作成

create table board(

id int not null auto_increment primary key,

name varchar(50),

comment text,

created datetime

);

データ型でtextを指定した場合、文字数が無制限になる。今回はPHP側でバリデーションをかける。投稿日時を記録するためのフィールドを「created」としたが、これはデータ型をdatetimeと指定している。「0000-00-00 00:00:00」という形で文字列を挿入しないとエラーになるので注意。

次に、先ほど用意したfunctions.phpに新しい関数を追2つ追加する。

function insert_comment($dbh,$name,$comment){
 //Insertクエリ実行
 $date=date('Y-m-d H:i:s');
 $sql="INSERT INTO board (name,comment,created) values (:name,:comment,'{$date}')";
 $stmt=$dbh->prepare($sql);
 $stmt->bindvalue(':name',$name,PDO::PARAM_STR);
 $stmt->bindvalue(':comment',$comment,PDO::PARAM_STR);
 if (!$stmt->execute()) {
  return 'データの書き込みに失敗しました。';
 }

これはコメントを書き込む関数である。データベース接続時に取得した$dbh(切符)を必ず渡すこと。$nameと$commentはPOSTデータを取得後に渡すものである。現在日時取得はデータベースに合わせ、「Y-m-d H:i:s」とすれば「0000-00-00 00:00:00」の形で取得できる。$dateはクライアントからの入力値ではないため、そのまま連結させることが可能。

function select_comments($dbh){
 $data=;
 $sql="SELECT name,comment,created FROM board";
 $stmt=$dbh->prepare($sql);
 $stmt->execute();
 while ($row=$stmt->fetch(PDO::FETCH_ASSOC)) {
  $data
=$row;
 }
 return $data;
}

データを取得する関数である。先ほどと同じく、$dbhを渡す。「select * from ~」と書くことにより、少しではあるがデータベースへの負担を軽くする。$dataは二次元配列、そのままreturnする。

ここから正式なサービスへ近づけていく。まずはindex.phpを作成し、functions.phpを組み込む。

<?php
 // 関数ファイルを読み込む
 repuire_once('./functions.php');

 

 // ここに処理の流れを書く

 

 // HTMLファイル読み込み

 include_once('view.php');

view.phpも空白でいいので作成しておく。view.phpにはHTMLを記述していくが、echoなども使うのでPHPファイルとして作成しておく。

require_once()とinclude_once()はともにファイルを読み込むコードだが、require_once()は読み込んだ途中のコードにエラーがあっても処理を続行し、include_once()はエラーがあれば処理が止まるという違いがある。

ここで処理の流れを確認。

  1. データベースに接続
  2. フォームのデータを取得してバリデーション
  3. データを挿入
  4. データベースから表示用のデータを引き出す
  5. 出力

特に3と4が逆にならないよう注意が必要である。

index.phpにデータベース接続のための処理を書いていく。

repuire_once('./functions.php');
$errs=; //エラー文格納用の配列初期化
$data=
; //データベースから引き出すデータの格納用配列
$dbh=get_db_connect(); //データベース接続 

 前はかなり長かったが、今回からはすっきり。

次に、入力値の検証とデータ挿入の処理を作成する。

$dbh=get_db_connect(); //データベース接続

if ($_SERVER['REQUEST_METHOD']==='POST') {
 $name=get_post('name');
 $comment=get_post('comment');
 if (!check_words($name,50)) {
  $errs='お名前欄を修正してください。';
 }
 if (!check_words($comment,200)) {
  $errs
='コメント欄を修正してください。';
 }
 if (count($errs)===0) {
  $result=insert_comment($dbh,$name,$comment);
 }

作成したget_post()でデータを種痘する。これでtrim()も自動的に行われる。check_words()でfalseが返ってきたらデータ挿入派でいない。$errsにエラーメッセージを格納し、$errsの要素が存在しない場合はinsert_data()を実行する。

次に、入力値の検証とデータ挿入を作成する。

if ($_SERVER['REQUEST_METHOD']==='POST') {
 //中略
}
$data=select_comments($dbh);
include_once('view.php');

データの取得は必ず行うので、if ($_SERVER['REQUEST_METHOD']==='POST')の外に書く。

最後にview.phpのプログラムを作成する。ここまでで表示データの取得は終えているので、出力に関わる部分を作成する。

<html>
 <body>
  <h1>ひとこと掲示板</h1>
  <table border="1">
   <tr style="background-color:orange"><th>名前</th><th>コメント</th><th>時刻</th></tr>
   <?php if (count($data)):
    foreach($data as $row): ?>
   <tr>
    <td><?php echo html_escape($row['name']); ?></td>
    <td><?php echo nl2br(html_escape($row['comment'])); ?></td>
    <td><?php echo $row['created']; ?></td>
   </tr>
  <?php endforeach;
   endif; ?>
  </table>
 <?php if(count($errs)) {
  foreach($errs as $err){
  echo '<p stype="color:red">'.$err.'</p>';
  }
 }?>

 <form action="" method="POST">
  <p>お名前*<input type="text" name="name">(50文字まで)</p>
  <p>ひとこと*<textarea name="comment" rows="4" cols="40"></textarea>(200文字まで)</p>
  <input type="submit" value="書き込む">
 </form>
 </body>
</html>

 $dataの要素が存在する場合はforeach()を使って出力する。include_once()ではboard.phpで種痘した$dataのデータは引き継がれている。フォームからの入力値は出力前に必ず無毒化する必要があるので、html_escape()を使用する。コメントには改行コードが含まれる可能性もあるので、さらにnl2br()も必要となる。エラーがあった場合はどこの入力欄にエラーがあったのかを赤文字で表示するようにする。

コードはこれで完成なので、index.phpにアクセスして動作をテストする。

f:id:R_de_aru:20180707204052p:plain

こんな感じで表示されるようになった。

今回はここまで。