PHP
Make ITDay 6

PHPのチュートリアルを作りたい!!

はじめに

MakeIT AdventCalendarの6日目のyamato3310です。
技術系のポエムがかけそうにないので、現在やっているPHPの振り返りとしてチュートリアルぽいものを書くことにします。
Qiitaへの投稿が初めてなのでおかしな点があると思いますが、温かい目で見てくれるとありがたいです!

データベースへの接続方法のチュートリアルを目標に書いています。
この記事は細かい解説よりも、todoリストを実際に作ってみてデータベースを触るような構成にしています。


環境はXAMMPを使っております。
インストールの仕方はこちらを参考にしてください。
【xampp】初心者でもわかるようにxampp入れてPHP+MySql環境作る説明する

手元で動作することは確認していますが、おかしな点などは指摘していただけると助かります。

最後に参考になったリンクも張ってあるので是非そちらも御覧ください。

今回使うコードを始めに貼っておきます。

<?php
class Todo {
    private $title;
    private $content;
    private $flg;

    public function __construct ($row) {
        $this->title = $row["title"];
        $this->content = $row["content"];
        $this->flg = 1;
    }

    //現在の自分のtodoリストを取得(flg == 1)
    public function get_todo () {
        global $pdo;
        $query = $pdo->query ("select * from todolist where flg = 1");
        $lists = $query->fetchAll (PDO::FETCH_ASSOC);
        return $lists;
    }

    //終了した自分のtodoリストを取得(flg == 0)
    public function end_todo () {
        global $pdo;
        $query = $pdo->query ("select * from todolist where flg = 0");
        $lists = $query->fetchAll (PDO::FETCH_ASSOC);
        return $lists;
    }

    //flgの変更
    public function update ($id) {
        global $pdo;
        $query = $pdo->prepare ("update todolist set flg = 0 where id = :id");
        $query->bindValue (":id", (int) $id);
        $query->execute ();
    }

    //リストを追加
    public function insert ($list) {
        global $pdo;
        $prepare = $pdo->prepare ("insert into todolist value ('', :title, :content, :flg)");
        $prepare->bindValue (":title", $list->title);
        $prepare->bindValue (":content", $list->content);
        $prepare->bindValue (":flg", $list->flg);
        $prepare->execute ();
    }

    //終了したリストの削除
    public function delete ($id) {
        global $pdo;
        $prepare = $pdo->prepare ("delete from todolist where id = :id");
        $prepare->bindValue (':id', (int) $id);
        $prepare->execute ();
    }
}

//DBとの接続
try {
    $pdo = new PDO (
        'mysql:host=localhost; dbname=todo; charset=utf8',
        'root',
        '',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false 
        ]
    );
} catch (PDOException $e) {
    exit ($e->getMessage ());
};

//($_POST['title']が入力されていたらリストに追加
if (isset ($_POST['title'])) {
    $list = new todo ($_POST);
    $list->insert ($list);
    header ('Location: http://localhost/ToDo/index.php');
};

//$_POST['id']が1のときの0に変更、0のときは削除
if (isset ($_POST['id'])) {
    if ($_POST['flg'] === '1') {
        todo::update ($_POST['id']);
    } else {
        todo::delete ($_POST['id']);
    };  
    header ('Location: http://localhost/ToDo/index.php');
};

?>

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel='stylesheet' href='index.css'>
</head>
<body>
    <ul class='ul'>
        <li>これからやること</li>
        <?php 
            $lists = todo::get_todo ();
            foreach ($lists as $list) {
        ?>
            <li class='todo'><?php echo htmlspecialchars ($list['title']) ."  " .htmlspecialchars ($list['content']); ?></li>
            <li>
                <form method='POST'>
                    <input type='hidden' value='<?php echo $list['id']; ?>' name='id'>
                    <input type='hidden' value='<?php echo $list['flg']; ?>' name='flg'>
                    <input type='submit' value='終了' class='todo_button'>
                </form>
            </li>
        <?php
            };
        ?>
    </ul>

    <ul class='ul'>
        <li>もうやったこと</li>
        <?php 
            $lists = todo::end_todo ();
            foreach ($lists as $list) {
        ?>
            <li class='todo'><?php echo htmlspecialchars ($list['title']) ."  " .htmlspecialchars ($list['content']); ?></li>
            <li>
                <form method='POST'>
                    <input type='hidden' value='<?php echo $list['id']; ?>' name='id'>
                    <input type='hidden' value='<?php echo $list['flg']; ?>' name='flg'>
                    <input type='submit' value='削除' class='todo_button'>
                </form>
            </li>
        <?php
            };
        ?>
    </ul>

    <form method="POST" class='form'>
        <input type="text" placeholder="タイトルを入力してね" name="title" class='title'>
        <textarea placeholder="内容を入力してね" name='content' class='content'></textarea>
        <input type="submit" value="追加" class='todo_button'>
    </form>
</body>
</html> 

今回はこれを使ってデータベースの使い方を解説していこうと思います!

テーブル設計

まずはデータベースを作ります

CREATE DATABASE `todo`;

使用するデータベースを指定します

USE `todo`;

あとはXAMMPのphpMyadminでコピペするだけです。

CREATE TABLE `todolist` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '番号',
    `title` VARCHAR(30) NOT NULL COMMENT 'タイトル',
    `content` VARCHAR(255) NOT NULL COMMENT '内容',
    `flg` INT NOT NULL COMMENT '1のときは実行中、0のときは終了したもの',
    PRIMARY KEY (`id`)
)DEFAULT CHARSET = utf8mb4;

データベースにアクセス

try {
    $pdo = new PDO (
        'mysql:host=localhost; dbname=todo; charset=utf8',
        'root',
        '',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false 
        ]
    );
} catch (PDOException $e) {
    exit ($e->getMessage ());
};

データベースへのアクセスはこの部分でやっています。基本こんな形です
データベースへ接続するときは、PDOインスタンスに設定を書いていきます。
詳しい説明は省略しますが、今回は接続するときのオプションである以下の2つの解説します

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
例外をスローしてくれます、スローをキャッチすることで例外が起きたときの処理を書くことができます。

PDO::ATTR_EMULATE_PREPARES => false
プリペアドステートメントのエミュレーション機能を使うかの設定です。

詳しくはこのページを参考してください 
PHPでデータベースに接続するときのまとめ

todoクラス

今回のtodoクラスには、自分のやる内容の投稿と削除する機能をつけています。

・get_todo () DBに保存されているflgが1の投稿を取得する
・end_todo () DBに保存されているflgが0の投稿を取得する
・update () 選択された投稿のflgを1から0に変更
・insert () 投稿する
・delete () 投稿を削除をする

この中で出てくるflgですが、はじめに投稿するとflgを1に、終了するとflgを0に変更しています。

クエリの発行

クエリの発行方法ですがget_todo ()を参考に説明します。

public static function get_todo () {
        global $pdo;
        $query = $pdo->query ("select * from todolist where flg = 1");
        $lists = $query->fetchAll (PDO::FETCH_ASSOC);
        return $lists;
    }

ユーザからの入力がない場合はPDO::queryメソッドを使用します。
結果は$listsに入ってきます。$listsの中身は以下のようになっています

    Array ( 
        [0] => Array ( 
                   [id] => 5 
                   [title] => title 
                   [content] => content 
                   [flg] => 1 
               )  
    )

ユーザーからの入力が含まれるときはプレースホルダーを使用します。
プレースホルダーを使用することで、自動でエスケープ処理をしてくれるので、SQLインジェクションの対策になります

 public static function update ($id) {
        global $pdo;
        $query = $pdo->prepare ("update todolist set flg = 0 where id = :id");
        $query->bindValue (":id", (int) $id);
        $query->execute ();
    }

:idがプレースホルダーで、$query->bindValue:idに引数で受け取っているidを埋め込んでいます。
$query->execute ();でクエリの実行をしています。

リストに追加と削除

//$_POST['title']が入力されていたらリストに追加
if (isset ($_POST['title'])) {
    $list = new todo ($_POST);
    $list->insert ($list);
    header ('Location: http://localhost/ToDo/index.php');
};

//$_POST['id']が1のときは0に変更、0のときは削除
if (isset ($_POST['id'])) {
    if ($_POST['flg'] === '1') {
        todo::update ($_POST['id']);
    } else {
        todo::delete ($_POST['id']);
    };  
    header ('Location: http://localhost/ToDo/index.php');
};

上のif文ではフォームからtitleが送らてれきた場合にやることを保存する処理をしています。
header ('Location: http://localhost/ToDo/index.php');の部分が何をしているのかと言うと、やることを保存する処理をした後にブラウザのリロードをすると同じ内容でPOST送信をしてしまうので、保存した後にGET送信でこのページにリダイレクトをすることでリロードをしてもPOST送信で保存されることを回避しています。
今回は、http://localhost/ToDo/index.phpとなっていますが、各自でこのファイルのパスを変更してください。
下のif文では、idがフォームから送られてきたときに一緒に送られてくるflgを確認してflgの変更をするか削除するかを判定しています。

画面に表示

 <?php 
            $lists = todo::get_todo ();
            foreach ($lists as $list) {
        ?>
            <li class='todo'><?php echo htmlspecialchars ($list['title']) ."  " .htmlspecialchars ($list['content']); ?></li>
            <li>
                <form method='POST'>
                    <input type='hidden' value='<?php echo $list['id']; ?>' name='id'>
                    <input type='hidden' value='<?php echo $list['flg']; ?>' name='flg'>
                    <input type='submit' value='終了' class='todo_button'>
                </form>
            </li>
        <?php
            };
        ?>

$lists = todo::get_todo ()で現在自分が保存しているやることをすべて取ってきて$listsに入れています。
後はforeach文で一つ一つ取ってきて、titleとcontentをくっつけたものと終了した時用のボタンを作っています。
htmlspecialchars ()ですがエスケープ処理をする関数です。
XSSの対策で、ユーザーからの入力は必ずしも意図したものが送信されるとは限らないのでしっかりとエスケープ処理をしましょう。

最後に

どうでしたか? 自分なりにデータベースへの接続の仕方をまとめとてみました。

次回の投稿ではlaravelでtodoリストを作る予定です。はい、またtodoリストを作ります。
次の日は自分よりも良い記事が上がると思うので是非そちらも。

自分がとても参考になったリンクを貼っておくので、ぜひこちらも参考にしてください。
【PHP超入門】クラス~例外処理~PDOの基礎

作ったtodoリストですがこのままだと味気ないので、こちらのcssを使ってみてください。

body {
    margin: 0;
    padding: 0;
}

input {
    display: block;
}

.content {
    width: 500px;
    height: 100px;
    margin: 0 auto;
    display: block;
}

.todo {
    float: left;
}

li {
    display: block;
}

.todo_button {
    background-color: white;
    margin: 0 auto;
}

.ul {
    background-color: #eeeeee;
    width: 1000px;
    height: 200px;
    text-align: center;
    margin: 50px auto;
}

.title {
    margin: auto;
    width: 500px;
}

.form {
    display: block;
    margin: 0 auto;
}