掲示板の作り方(データベース使用)

PHPを使う掲示板の中でも、データベースを使用したタイプの作り方をメモ。(注:同じimg名の重複を回避できないし、imgの日本語名にも未対応)

データベースの作成

データベースは、XAMPPでPHPと一緒にインストールできるMySQLを使用します。MySQLの編集はXAMPPのphpMyAdminを使って簡易的に行います。

1、データベースを作成する。今回はkeijbanというデータベースを作成。

2、データベースにテーブルを作成する。今回はkeiji1というテーブルを作成。

3、テーブルにカラムを作成する。作成したカラムの内容やカラム数は後で自由に変更できるので、ここでは適当に作成すればよいかと思います。

データベースへの接続とフォームの作成

mysql_connect()を使用して、mysql_select_dbで指定したデータベースへ接続します。newstring()関数を作成して、フォームに入力された内容のチェックを簡便に行えるようにしておく。

//php内
<?php 
session_start();

@$title= "掲示板サンプル";
@$url= "homepage/keiji.php";
list($urlbe,$urlaf)=explode("/",$url,2); //explodeで2つに分割し、listで2つの変数に格納する。

mysql_connect('localhost','root','') or die(mysql_error());
mysql_select_db('keijban');
mysql_query('set names utf8');

function newstring($string) {
    if(get_magic_quotes_gpc()){ //php.iniのget_magic_quotesがtrueなら
    $string = stripslashes($string); //シングル、ダブルクオートのバックスラッシュを取り除く
    }
$string = htmlspecialchars($string,ENT_QUOTES,'utf-8'); //htmlの特定のタグ無効化
$string = str_replace(",",",",$string); //半角カンマを全角に変換
$string = str_replace(array("\r\n","\n","\r"),"<br>",$string); //改行タグをbrに変換
return $string;
}
?>

htmlの構文を記述する。head内はテンプレ的に。body内に各種フォームを並べる。

formタグのmethodはpostで、actionは空にして自分自身に送る。enctype="multipart/form-data"は$_FILEでファイルデータを受け取るために必要な記述。逆に言えばファイルを送らないなら不要な記述。

inputタグのvalue値には$_POSTの内容を入れる。初期は値がないが、のちに入力項目のチェックを行うときに記入済み項目内容を残して置けるように。

inputの個別項目の説明は、フォームの種類を参照。

アップロードできるファイルサイズやファイル云々については、ファイル操作を参照。

<!--HTML-->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="<?php echo($title); ?>について解説しています" />
<meta name="keywords" content="管理薬剤師,薬局,業務,<?php echo($title); ?>" />
<meta name="author" content="wakabagari" />
<meta name="viewport" content="width=device-width" />
<meta name="format-detection" content="telephone=no" />
<title><?php echo($title); ?></title>
<style>
<!--
span {
    color:#f00;
    }
-->
</style>
</head>

<body>
<form id="x1" name="x1" method="post" action="" enctype="multipart/form-data">
<dl>
<dt><label for="name">名前(10文字まで)<span>※必須</span></label></dt>
<dd><input type="text" name="name" id="name" size="50" maxlength="10" value="<?php echo newstring(@$_POST['name']) ?>" /></dd>
<dt><label for="sub">件名(20文字まで)<span>※必須</span></label></dt>
<dd><input type="text" name="sub" id="sub" size="50" maxlength="20" value="<?php echo newstring(@$_POST['sub']) ?>" /></dd>
<dt><label for="body">インプットエリア(1000文字まで)<span>※必須</span></label></dt>
<dd><textarea id="body" name="body" cols="50" rows="5" maxlength="1000"><?php echo newstring(@$_POST['body']) ?></textarea></dd>

<dt><label for="upfile">アップする画像ファイル<br />(ファイル名:<span>日本語名不可</span>半角英数字のみ、サイズ:<span>2Mまで</span>)</label></dt>
<dd><input type="file" name="upfile" /></dd>

<dt><label for="password">パスワード<img src="./img/88.png" alt="" style="vertical-align:middle;" /></label></dt>
<dd><input type="password" id="pass" name="pass" size="10" maxlength="4" /></dd>
</dl>
<input type="submit" value="書き込み" />
</form>

</body>
</html>

フォーム入力内容のチェック

mysql_connect()を使用して、mysql_select_dbで指定したデータベースへ接続します。newstring()関数を作成して、フォームに入力された内容のチェックを簡便に行えるようにしておく。

そのためにphpスクリプトとして以下を加える。

if(isset($_POST)){ //$_POSTがtrueなら。falseは未定義かnullかで、0空文字はtrue。
    if(@$_POST['name']==''){ //nameが空文字なら
        $error['name'] = 'blank';
    }
    if(@$_POST['sub']==''){
        $error['sub'] = 'blank';
    }
    if(@$_POST['body']==''){
        $error['body'] = 'blank';
    }
    if(strlen(@$_POST['pass']) < 4){
        $error['pass'] = 'len_error';
    }
    if(@$_POST['pass']==''){
        $error['pass'] = 'blank';
    }
    if(empty($error)){ //$errorが0or空文字、つまりエラー(未入力)がなければ、
        $_SESSION['join'] = $_POST; //$_POSTの内容をセッションに記録しておく。
    }
        if(!@$_SESSION['jikan']){ //SESSION['jikan']がfalseなら、1を代入しておく
            $_SESSION['jikan']=1;
        }
        if($_SESSION['jikan'] < time()){ //SESSION['jikan']が現在の時間(time()は現在の時刻をタイムスタンプで返す)よりも小さいなら、
            $save_dir="../../file/img/"; //ファイルを保存するディレクトリ
            $filemei=newstring(@$_FILES["upfile"]["name"]); //$_FILES["upfile"]["name"]でアップされたファイル名(正式名称)を取得
            $filetmp=newstring(@$_FILES["upfile"]["tmp_name"]); //$_FILES["upfile"]["tmp_name"]でアップされた一時ファイルのファイルパス
            $file_check=substr($filemei,-3); //ファイル名の最後の3文字を抜き出す
            if($file_check==("jpg"||"JPG"||"jpeg"||"png"||"gif"||"bmp")){
                $file_name=$save_dir.$filemei; //ファイル名(ディレクトリを含める)
                if (is_uploaded_file($filetmp)) { //is_uploaded_fileでファイルがアップロードされたかどうかを調べる
                //copy($filetmp,$file_name); //テンポラリファイルをファイルにコピーする
                move_uploaded_file($filetmp,$file_name); //テンポラリファイルを、指定した場所へコピーする。
                }
            }
        }

}

フォームの中身に入力エラーが起こった場合に、「入力して下さい」の文字がinputエリア横に表示されるようにする。上記PHPスクリプトで未入力がある項目を「blank」としてerror配列に格納してあるので、配列内がblankになっていたら文字が表示されるようにする。

if文のendは、endif;を使う。

アップロードするファイルの拡張子もこの時確認して、画像以外のファイルをアップロード出来ないようにする。まぁ、拡張子を変えれば出来てしまいますが。。。

<form id="x1" name="x1" method="post" action="" enctype="multipart/form-data">
<dl>
<dt><label for="name">名前(10文字まで)<span>※必須</span></label><?php if(@$error['name']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><input type="text" name="name" id="name" size="50" maxlength="10" value="<?php echo newstring(@$_POST['name']) ?>" /></dd>
<dt><label for="sub">件名(20文字まで)<span>※必須</span></label><?php if(@$error['sub']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><input type="text" name="sub" id="sub" size="50" maxlength="20" value="<?php echo newstring(@$_POST['sub']) ?>" /></dd>
<dt><label for="body">インプットエリア(1000文字まで)<span>※必須</span></label><?php if(@$error['body']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><textarea id="body" name="body" cols="50" rows="5" maxlength="1000"><?php echo newstring(@$_POST['body']) ?></textarea></dd>

<dt><label for="upfile">アップする画像ファイル<br />(ファイル名:<span>日本語名不可</span>半角英数字のみ、サイズ:<span>2Mまで</span>)</label></dt>
<dd><input type="file" name="upfile" /></dd>

<dt><label for="password">パスワード<img src="./img/88.png" alt="" style="vertical-align:middle;" /></label><?php if(@$error['pass']=='blank'): ?><span> 左の4桁の数字を入力してください</span><br /><?php endif; ?><?php if(@$error['pass']=='len_error'): ?><span> 半角4文字で入力してください</span><?php endif; ?></dt>
<dd><input type="password" id="pass" name="pass" size="10" maxlength="4" /></dd>
</dl>
<input type="submit" value="書き込み" />
</form>

データベースへの書き込みと連続投稿の防止

if文の中にSQLを実行する構文を追加する。

「INSERT INTO テーブル名 SET key1="値",key2="値"」でデータベースに値を追加することが出来る。

この時SQLインジェクションを防止するために、mysql_real_escape_string()を使用しておかなければならないため、SET name=mysql_real_escape_string($_SESSION['join']['name'])の様な形にする必要がある。項目が多く、見づらくならないようにsprintf()を使って見やすく整形する方法が取られる。

SQLの命令自体は、mysql_query()で行う。

        if($_SESSION['jikan'] < time()){ 
	・
	・
	・

            $sql = sprintf("INSERT INTO keiji1 SET name='%s',sub='%s',body='%s',title='%s',url='%s',img='%s',created=NOW()", //NOW()はmySQLの関数。date(y-m-d h:i:s)でもいい
            mysql_real_escape_string($_SESSION['join']['name']), //mysqlに値をセットする時はSQLインジェクションの制御を行う。
            mysql_real_escape_string($_SESSION['join']['sub']),
            mysql_real_escape_string($_SESSION['join']['body']),
            $title,$url,
            mysql_real_escape_string($_FILES["upfile"]["name"])
            );
            mysql_query($sql) or die(mysql_error()); //mysql_query()で実行する。

        $jikan=time()+20; //$jikanを現在のタイムスタンプ+20秒にしてそれをセッションに記録する
        $_SESSION['jikan']=$jikan;

        unset($_SESSION['join']); //sessionのjoinの値を削除する
        @$jumpurl="Location:".$urlaf; //飛ばすURLを自分自身に設定
        header($jumpurl); //headerで飛ばす
        exit(); //exit()とdie()はphpスクリプトを終了する命令
        }else{ //投稿から20秒経過していないなら
            echo("<p class='b3'><span>連続投稿不可。時間をおいて投稿してください。</span></p><br /><br />");
        }

ついでに連続投稿を防止するために、先に記録しておいた$jikanに現在の時間に20秒を加えた値を加えて、この値をセッションに記録しておく。こうすることで、再度書き込みを行うときに$jikanの値と現在の時間を比較して連続投稿を防止することが出来る。

ついでに、データベースに書き込んで不要となったセッションデータはunset()命令で削除し、header()で同ページに送る(つまり、再読み込みをして投稿を表示させる)。ここでスクリプトを終了させる。

書き込んだデータの表示

データベースに書き込んだデータをデータベースから引き出す命令が、SELECT文です。下記PHPスクリプトを追加します。

最初のSELECT文で全レコード数を取得し、keyをcntに設定、mysql_fetch_assoc()でdetaを配列で取り出して$kensuに入れる。$kensu['cnt']で件数を取り出せる。これを使って全ページ数を算出できる。

記事の表示は、またSELECT文で。値は配列に格納されるので、それをwhile文で順番に画面に表示させる。

<?php 
$data = mysql_query("SELECT COUNT(*) AS cnt FROM keiji1 k where k.url='".$url."'") or die(mysql_error()); //テーブル名keiji1のurlが$urlと同じレコード件数を調べる。
$kensu = mysql_fetch_assoc($data); //件数を配列で変数に格納する。

if(empty($page)){ //$pageが空(0または"")なら
    $page=1;
    }
if(!empty($_REQUEST['page'])){ //$_REQUEST['page']が空(0または"")でないなら
    $page=$_REQUEST['page']; //$pageにページ数を代入
}

if($kensu['cnt']<1){//件数が0の場合に$endがマイナスエラーになるので。
    $kensu['cnt']=1;
    }

$page=max($page,1); //$pageと1でどちらか大きい方。2pなら2
$maxPage=ceil($kensu['cnt']/10); //最大ページはレコード数÷10を切り上げた数
$page=min($page,$maxPage); //$pageと最大ページどちらか小さい方。最大ページが5、ページが6なら5にする。

$start=$page*10-10;//開始位置を測るための変数
$end=min($kensu,$page*10);//今いるページが1ページ目なら5、2ページ目なら10まで。(終了位置を測るための変数)

$recordSet = mysql_query("SELECT * FROM keiji1 k where k.url='".$url."' ORDER BY k.id DESC LIMIT ".$start.",10") or die(mysql_error());

while($datas=mysql_fetch_assoc($recordSet)){ //配列として取り出せるデータが有る間。順々に$datasに値を入れる。

echo("<p class='p2'>記事No".newstring($datas['id'])." 題名:".newstring($datas['sub'])." 投稿者:".newstring($datas['name'])." 投稿日:".newstring($datas['created'])."</p><p class='p3'>".newstring($datas['body'])."</p>");

if(!empty($datas['img'])){ //imgデータが0または""でなければ、画像を表示させる。
    echo("<figure class='p4'><img src='https://localhost/img/".newstring($datas['img'])."' alt='' /></figure>");
}
echo("<hr />");
//imgのURLは絶対パスじゃないとだめ。../../fileだと、最上位の階層までは戻れないみたい。
}
?>
<ul class="clearfix p1">
<?php 
if($page > 1){
?>
<li><a href="<?php echo($urlaf); ?>?page=<?php echo($page-1);?>">&lt;&lt;&nbsp;前のページ</a></li>
<?php 
}else{
?>
<li>&lt;&lt;&nbsp;前のページ</li>
<?php 
}

if($page < $maxPage){
?>
<li><a href="<?php echo($urlaf); ?>?page=<?php echo($page+1);?>">次のページ&nbsp;&gt;&gt;</a></li></ul>
<?php 
}else{
?>
<li>次のページ&nbsp;&gt;&gt;</li>
</ul>
<?php 
}
?>

完成形

上記を全て反映させた完成形が以下。

<?php 
session_start();

@$title="掲示板サンプル";
@$url="homepage/keiji.php";
list($urlbe,$urlaf)=explode("/",$url,2); //explodeで2つに分割し、listで2つの変数に格納する。

mysql_connect('localhost','root','') or die(mysql_error());
mysql_select_db('keijban');
mysql_query('set names utf8');

function newstring($string) {
    if(get_magic_quotes_gpc()){ //php.iniのget_magic_quotesがtrueなら
    $string = stripslashes($string); //シングル、ダブルクオートのバックスラッシュを取り除く
    }
$string = htmlspecialchars($string,ENT_QUOTES,'utf-8'); //htmlの特定のタグ無効化
$string = str_replace(",",",",$string); //半角カンマを全角に変換
$string = str_replace(array("\r\n","\n","\r"),"<br>",$string); //改行タグをbrに変換
return $string;
}




if(isset($_POST)){ //$_POSTがtrueなら。falseは未定義かnullかで、0空文字はtrue。
    if(@$_POST['name']==''){ //nameが空文字なら
        $error['name'] = 'blank';
    }
    if(@$_POST['sub']==''){
        $error['sub'] = 'blank';
    }
    if(@$_POST['body']==''){
        $error['body'] = 'blank';
    }
    if(strlen(@$_POST['pass']) < 4){
        $error['pass'] = 'len_error';
    }
    if(@$_POST['pass']==''){
        $error['pass'] = 'blank';
    }
    if(empty($error)){ //$errorが0or空文字、つまりエラー(未入力)がなければ、
        $_SESSION['join'] = $_POST; //$_POSTの内容をセッションに記録しておく。
    }

    if((@$_POST["pass"]==7248)&&(isset($_SESSION['join']))){
        if(!@$_SESSION['jikan']){ //SESSION['jikan']がfalseなら、1を代入しておく
            $_SESSION['jikan']=1;
        }
        if($_SESSION['jikan'] < time()){ //SESSION['jikan']が現在の時間(time()は現在の時刻をタイムスタンプで返す)よりも小さいなら、
            $save_dir="./img/"; //ファイルを保存するディレクトリ
            $filemei=newstring(@$_FILES["upfile"]["name"]); //$_FILES["upfile"]["name"]でアップされたファイル名(正式名称)を取得
            $filetmp=newstring(@$_FILES["upfile"]["tmp_name"]); //$_FILES["upfile"]["tmp_name"]でアップされた一時ファイルのファイルパス
            $file_check=substr($filemei,-3); //ファイル名の最後の3文字を抜き出す
            if($file_check==("jpg"||"JPG"||"jpeg"||"png"||"gif"||"bmp")){
                $file_name=$save_dir.$filemei; //ファイル名(ディレクトリを含める)"./img/ファイル名"
                if (is_uploaded_file($filetmp)) { //is_uploaded_fileでファイルがアップロードされたかどうかを調べる
                //copy($filetmp,$file_name); //テンポラリファイルをファイルにコピーする
                move_uploaded_file($filetmp,$file_name); //テンポラリファイルを、指定した場所へコピーする。
                }
            }

            $sql = sprintf("INSERT INTO keiji1 SET name='%s',sub='%s',body='%s',title='%s',url='%s',img='%s',created=NOW()", //NOW()はmySQLの関数。date(y-m-d h:i:s)でもいい
            mysql_real_escape_string($_SESSION['join']['name']), //mysqlに値をセットする時はSQLインジェクションの制御を行う。
            mysql_real_escape_string($_SESSION['join']['sub']),
            mysql_real_escape_string($_SESSION['join']['body']),
            $title,$url,
            mysql_real_escape_string($_FILES["upfile"]["name"])
            );
            mysql_query($sql) or die(mysql_error()); //mysql_query()で実行する。

        $jikan=time()+20; //$jikanを現在のタイムスタンプ+20秒にしてそれをセッションに記録する
        $_SESSION['jikan']=$jikan;

        unset($_SESSION['join']); //sessionのjoinの値を削除する
        @$jumpurl="Location:".$urlaf; //飛ばすURLを自分自身に設定
        header($jumpurl); //headerで飛ばす
        exit(); //exit()とdie()はphpスクリプトを終了する命令
        }else{ //投稿から20秒経過していないなら
            echo("<p class='b3'><span>連続投稿不可。時間をおいて投稿してください。</span></p><br /><br />");
        }
    }

}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="<?php echo($title); ?>について解説しています" />
<meta name="keywords" content="管理薬剤師,薬局,業務,<?php echo($title); ?>" />
<meta name="author" content="wakabagari" />
<meta name="viewport" content="width=device-width" />
<meta name="format-detection" content="telephone=no" />
<title><?php echo($title); ?></title>
<style>
<!--
span {
    color:#f00;
    }
-->
</style>
</head>

<body>

<form id="x1" name="x1" method="post" action="" enctype="multipart/form-data">
<dl>
<dt><label for="name">名前(10文字まで)<span>※必須</span></label><?php if(@$error['name']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><input type="text" name="name" id="name" size="50" maxlength="10" value="<?php echo newstring(@$_POST['name']) ?>" /></dd>
<dt><label for="sub">件名(20文字まで)<span>※必須</span></label><?php if(@$error['sub']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><input type="text" name="sub" id="sub" size="50" maxlength="20" value="<?php echo newstring(@$_POST['sub']) ?>" /></dd>
<dt><label for="body">インプットエリア(1000文字まで)<span>※必須</span></label><?php if(@$error['body']=='blank'): ?><span> 入力してください</span><?php endif; ?></dt>
<dd><textarea id="body" name="body" cols="50" rows="5" maxlength="1000"><?php echo newstring(@$_POST['body']) ?></textarea></dd>

<dt><label for="upfile">アップする画像ファイル<br />(ファイル名:<span>日本語名不可</span>半角英数字のみ、サイズ:<span>2Mまで</span>)</label></dt>
<dd><input type="file" name="upfile" /></dd>

<dt><label for="password">パスワード<img src="./img/88.png" alt="" style="vertical-align:middle;" /></label><?php if(@$error['pass']=='blank'): ?><span> 左の4桁の数字を入力してください</span><br /><?php endif; ?><?php if(@$error['pass']=='len_error'): ?><span> 半角4文字で入力してください</span><?php endif; ?></dt>
<dd><input type="password" id="pass" name="pass" size="10" maxlength="4" /></dd>
</dl>
<input type="submit" value="書き込み" />
</form>

<?php 
$data = mysql_query("SELECT COUNT(*) AS cnt FROM keiji1 k where k.url='".$url."'") or die(mysql_error()); //テーブル名keiji1のurlが$urlと同じレコード件数を調べる。
$kensu = mysql_fetch_assoc($data); //件数を配列で変数に格納する。

if(empty($page)){ //$pageが空(0または"")なら
    $page=1;
    }
if(!empty($_REQUEST['page'])){ //$_REQUEST['page']が空(0または"")でないなら
    $page=$_REQUEST['page']; //$pageにページ数を代入
}

if($kensu['cnt']<1){//件数が0の場合に$endがマイナスエラーになるので。
    $kensu['cnt']=1;
    }

$page=max($page,1); //$pageと1でどちらか大きい方。2pなら2
$maxPage=ceil($kensu['cnt']/10); //最大ページはレコード数÷10を切り上げた数
$page=min($page,$maxPage); //$pageと最大ページどちらか小さい方。最大ページが5、ページが6なら5にする。

$start=$page*10-10;//開始位置を測るための変数
$end=min($kensu,$page*10);//今いるページが1ページ目なら5、2ページ目なら10まで。(終了位置を測るための変数)

$recordSet = mysql_query("SELECT * FROM keiji1 k where k.url='".$url."' ORDER BY k.id DESC LIMIT ".$start.",10") or die(mysql_error());

while($datas=mysql_fetch_assoc($recordSet)){ //配列として取り出せるデータが有る間。順々に$datasに値を入れる。

echo("<p class='p2'>記事No".newstring($datas['id'])." 題名:".newstring($datas['sub'])." 投稿者:".newstring($datas['name'])." 投稿日:".newstring($datas['created'])."</p><p class='p3'>".newstring($datas['body'])."</p>");

if(!empty($datas['img'])){ //imgデータが0または""でなければ、画像を表示させる。
    echo("<figure class='p4'><img src='https://localhost/img/".newstring($datas['img'])."' alt='' /></figure>");
}
echo("<hr />");
//imgのURLは絶対パスじゃないとだめ。../../fileだと、最上位の階層までは戻れないみたい。
}
?>
<ul class="clearfix p1">
<?php 
if($page > 1){
?>
<li><a href="<?php echo($urlaf); ?>?page=<?php echo($page-1);?>">&lt;&lt;&nbsp;前のページ</a></li>
<?php 
}else{
?>
<li>&lt;&lt;&nbsp;前のページ</li>
<?php 
}

if($page < $maxPage){
?>
<li><a href="<?php echo($urlaf); ?>?page=<?php echo($page+1);?>">次のページ&nbsp;&gt;&gt;</a></li></ul>
<?php 
}else{
?>
<li>次のページ&nbsp;&gt;&gt;</li>
</ul>
<?php 
}
?>

コメントor補足情報orご指摘あればをお願いします。

(件名or本文内でキーワード検索できます)



記事No1411 題名:テスト 投稿者:テスト 投稿日:2020-08-04 05:50:37

テストです


記事No1353 題名:テスト 投稿者:はい 投稿日:2020-06-17 02:46:33

テストです


記事No1085 題名:test 投稿者:test 投稿日:2019-09-21 14:53:17

etst


記事No1033 題名:あ 投稿者:なまえ 投稿日:2019-08-12 16:52:03


記事No893 題名:あ 投稿者:あ 投稿日:2019-03-07 02:29:46


ページトップへ