0
0

More than 1 year has passed since last update.

読書ログアプリケーションの作成(独学エンジニア レッスン6より)

Posted at

目標

今回やること

  • Webアプリケーションを作成する

タスクばらし

  • 必須知識
  • 設計・テーブル作成
  • [登録ページ] HTMLで表示する
  • [登録ページ] 読書ログを登録する
  • [登録ページ] CSSでスタイルを整える
  • [トップページ] HTMLで表示する
  • [トップページ] 読書ログの一覧を表示する
  • [トップページ] CSSでスタイルを整える

必須知識

目的

  • PHPで開発するにあたって必須な知識を学ぶ

開発の基本順序

  • 設計→テーブル作成→HTML→ロジック処理→CSS

Composer

  • 現状の課題
    機密データがソースコードにベタ書き
    ソースコードを誰かに見られたら機密データが漏洩し、データが流出する

  • 解決策
    ライブラリを使用して、機密データを別途管理できる仕組みを入れよう

  • ライブラリ
    使いたい部品が入っている道具箱
    特定の機能を提供するコードをひとまとめにしたもの

ex)機密データ管理ライブラリ、ログ管理ライブラリ

  • PHPでライブラリを導入したい時はComposerを使うと便利。
    ComposerはPHPの依存管理ツール

何も使わないと、
1, 一つのライブラリを使うのに他のライブラリをいちいちインストールしてこないといけない
2, チーム開発時に人によって使用しているライブラリのバージョンがバラバラ。それが起因でエラーになる。

Composerを使うと、
1, プロジェクトが使用しているライブラリとそのバージョンを統一できる
2, ライブラリが依存しているライブラリもセットでインストールできる
3, プロジェクトごとにライブラリを管理できる

composer.jsonとcomposer.lockファイルで依存するライブラリを定義する

  • composer.json
    プロジェクトで使用するライブラリを一覧にしたもの

  • composer.lock
    実際にどのライブラリのどのバージョンをダウンロードしたかをひとまとめにしたもの

1, coposer installを実行すると、インストールしたライブラリとバージョン情報が書き出される
2, composer.lockがある状態でcomposer installすると、composer.lockに記されたバージョンのライブラリがインストールされる

よって、チーム内で同じバージョンのライブラリを使える

コマンド流れ

  • init
    対話形式でcomposer.jsonを作成する(プロジェクトの初期に一度実行)
linux
composer init
  • require
    新しいライブラリを追加する
    composer.jsonにライブラリが、  composer.lockに実際にインストールしたものが記載される
linux
composer require <ライブラリ名>
  • install
    composer.jsonもしくはcomposer.lockに従ってライブラリをインストールする
    チーム開発時に使用。共通のバージョンのライブラリを使うことができる
linux
composer install
  • remove
    ライブラリを取り除く
linux
composer remove <ライブラリ名>

環境変数
機密データをソースコードから分離するのに環境変数を使う

  • 通常の変数
    PHPのコードの中で定義し、実行したプロセス内でのみ使用できる

  • 環境変数
    PHPが動いてるOSに依存し、PHPアプリケーションに渡す仕組み(OSに格納される変数)
    PHPコードに直接記述しないので、コードを見ても変数の中身がバレず、また環境によって切り替えられる

環境変数を使うのに、.envファイルを使うと簡単に設定できて便利(phpdotenv)

現状

  • ソースコードにベタ書き
linux
$link = mysqli_connect('db', 'book_log', 'pass', 'book_log');

env使うと

  • ソースコードから分離できる。環境によっても使い分けられる
    envファイル:重要な設定データを定義する→環境変数として定義される
    DB_HOST=db
  • プログラムのファイル:.envファイルに定義された値を取得できる
linux
$dbHost = getenv('DB_HOST'); //$_ENV['DB_'HOST]でも取得できる
$link = mysqli_connect($dbHost, ..., ..., ,,,);

Webページが表示される仕組み

Webページが表示される流れを銀行に例えると、

  • お客さん
    ブラウザ
  • 窓口
    apache
  • 手続きなど作業する人
    PHP
  • 作業する人があつかうデータ
    データベース

設計・テーブル作成

目的

  • テーブルの初期化をPHPから実施する(毎回SQL文を入力するのは面倒なため)

必要な知識

  • 特になし

実践

  • テーブルを初期化する処理をinitialize_reviews_table.phpに記載
initialize_reviews_table.php
<?php

require_once __DIR__ . '/lib/mysqli.php';

function dropTable($link)
{
    $dropTable = 'DROP TABLE IF EXISTS reviews;';
    $result =  mysqli_query($link, $dropTable);
    if ($result) {
        echo 'テーブルの削除に成功しました' . PHP_EOL;
    } else {
        echo 'Error: テーブルの削除に失敗しました' . PHP_EOL;
        echo 'Debugging error: ' . mysqli_error($link)  . PHP_EOL;
    }
}

function createTable($link)
{
    $createTable = <<<EOT
CREATE TABLE reviews (
    id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
    title VARCHAR(255),
    author VARCHAR(100),
    status VARCHAR(10),
    score INTEGER,
    summary VARCHAR(1000),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) DEFAULT CHARACTER SET=UTF8MB4;
EOT;
    $result =  mysqli_query($link, $createTable);
    if ($result) {
        echo 'テーブルの作成に成功しました' . PHP_EOL;
    } else {
        echo 'Error: テーブルの作成に失敗しました' . PHP_EOL;
        echo 'Debugging error: ' . mysqli_error($link)  . PHP_EOL;
    }
}

$link = dbConnect();
dropTable($link);
createTable($link);
mysqli_close($link);

  • 該当のディレクトリで以下コマンドを実施するとテーブルの初期化が実施できる
linux
docker-compose exec app php reviews/initialize_reviews_table.php

[登録ページ] HTMLで表示する

目的
PHPでHTMLを表示する

必要な知識

  • HTML、CSS、PHPの作成手順
    HTML→PHP→CSS

  • HTMLを書くときの順序
    1, WFを書く
    2, HTMLで基本構成とheadを作成する
    3, 大枠のレイアウトをメモする
    4, HTMLで大枠のレイアウトを作成する
    5, 個別パーツのレイアウトをメモする
    6, HTMLで個別パーツを作成する

※HTMLを書くときのコツは入れ子構造を意識すること

実践

  • 表示したい内容のHTMLをファイルに記載

  • ブラウザのURL欄に以下を記載する

localhost:アプリケーションのポート番号/表示したいファイルのルートからのフルパス

  • src/reviews/new.phpに記載
new.php
<body>
    <h1>読書ログ</h1>
    <form action="" method="post">
        <div>
            <label for="title">書籍名</label>
            <input type="text" name="title" id="title">
        </div>
        <div>
            <label for="author">著者名</label>
            <input type="text" name="author" id="author">
        </div>
        <div>
            <label>読書状況</label>
            <div>
                <div>
                    <input type="radio" name="status" id="status1" value="未読">
                    <label for="status1">未読</label>
                </div>
                <div>
                    <input type="radio" name="status" id="status2" value="読んでる">
                    <label for="status2">読んでる</label>
                </div>
                <div>
                    <input class="form-check-input" type="radio" name="status" id="status3" value="読了">
                    <label for="status3">読了</label>
                </div>
            </div>
        </div>
        <div>
            <label for="score">評価5点満点の整数</label>
            <input type="number" name="score" id="score">
        </div>
        <div>
            <label for="summary">感想</label>
            <textarea type="text" name="summary" id="summary" rows="10"></textarea>
        </div>
        <button type="submit">登録する</button>
    </form>
</body>

[登録ページ] 読書ログを登録する

目的
PHPでデータを登録する

必要な知識

PHPで定義されている変数。外部から情報などが取得できる

  • $_ENV
    環境変数
    連想配列として受け取る

  • $_POST
    POSTされた情報
    連想配列として受け取る

  • $_GET
    URLパラメータの情報
    連想配列として受け取る

  • $_SERVER
    連想配列として受け取る
    REQUEST_METHOD:ページにアクセスする際に使用されたリクエストのメソッド名。''GET', 'POST'など

マジック定数

  • DIR
    そのファイルの存在するディレクトリ名

  • FILE
    ファイルのフルパスとファイル名

  • LINE
    そのファイル上の現在の行番号

共通の処理は別ファイルに一箇所にまとめて、そのファイルを読み込んで使おう

  • require
    指定したファイルを読み込む
    読み込みが失敗すると処理がそこで中断される

  • require_once
    requireとほぼ同じだが、一度しかファイルを読み込まない
    悩んだらrequire_onceを使うことを推奨

リダイレクト
ユーザを自動的に別のページヘ転送する仕組み
1つのページに1つの処理

  • header
    生のHTTPヘッダを送信する
php
$headerヘッダ文字列
header($header)
  • リダイレクトするには"Location:"ヘッダを付ける
    リダイレクト処理するとこれ以降のコードは実行されないので注意
php
header("Location: http://www.example.com/");

エラーハンドリング

  • エラーメッセージをログに書き出す
    ログは、障害などが起きた時に何が起きたかを把握できるように、プログラムの実行状況・データ送受信状況などを記録しておくもの

  • PHP・Apacheのログ
    ログには主にアクセスログとエラーログがある(自分で設定できる)。
    PHPはエラーログ、Apacheはアクセスログとエラーログを主に出力

  • アクセスログ
    1, ブラウザがサーバー(Apache)にリクエストし、それにApacheが応えるごとに記録される(アクセスのたびに記録)
    2, アクセスの履歴を確認するために使う

  • エラーログ
    1, リクエストの結果がエラーになったものだけが記録される
    2, サーバに問題が起きたら検知するために使う

  • error_log
    エラーメッセージをWebサーバーのエラーログに送信する
    error_log($message)

「データベースに接続できません」と記録するなら

php
error_log("データベースに接続できません");
  • Dockerコンテナ内のログを確認するにはdocker logsコマンドを使う

事前準備:コンテナ名を確認

linux
docker ps

ログを確認したい時(-f:follow。ログの出力を表示し続ける)

linux
docker logs -f <コンテナ名>

アクセスログを確認したい時(エラーログを捨てる)

linux
docker logs <コンテナ名> -f 2>/dev/null

エラーログを確認したい時

linux
docker logs <コンテナ名> -f 1>/dev/null

PHPとHTMLファイルを分割する

  • include
    指定したファイルを読み込む
    読み込みが失敗しても処理が中断されない

※require_onceと似ているが、処理が中断されない点が異なる。HTMLやテキストなどを読み込む時に使う
※ロジックとHTMLはファイル分割する
※DRY
Don' t repeat yourself

バリデーションエラーしても入力値を残す

  • value属性
    input要素の値を指定する属性
    テキスト入力欄などにおいて初期入力値を表す
php
<input type="text" id="name" name="name" value="<?php echo $company['name'] ?>">
  • バリデーションエラー時に入力した値が消える理由は、新しくHTMLを読み込んでいるから。これを改善するにフォームの初期値を設定しておく方法をパーツごとに抑えとくとよい。

  • checked属性を付けると初期値で選択された状態になる

php
<div>
	// 「男性」が最初から選択された状態になる
	<input type="radio" name="sex" id="sex1" value="男性" checked>
	<label for="sex1">男性</label>
</div>
<div>
	<input type="radio" name="sex" id="sex2" value="女性">
	<label for="sex2">女性</label>
</div>

<div>
	<input type="radio" name="sex" id="sex1" value="男性"
		<?php echo ($user['sex'] === '男性') ? 'checked' : ''; ?>>
	<label for="sex1">男性</label>
</div>

実践

src/reviews/lib/mysqli.php に共通処理をまとめる。

mysqli.php
<?php
require __DIR__ . '/../vendor/autoload.php';

function dbConnect()
{
    $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
    $dotenv->load();

    $dbHost = $_ENV['DB_HOST'];
    $dbUsername = $_ENV['DB_USERNAME'];
    $dbPassword = $_ENV['DB_PASSWORD'];
    $dbDatabase = $_ENV['DB_DATABASE'];

    $link = mysqli_connect($dbHost, $dbUsername, $dbPassword, $dbDatabase);
    if (!$link) {
        echo 'Error: データベースに接続できません' . PHP_EOL;
        echo 'Debugging error: ' . mysqli_connect_error() . PHP_EOL;
        exit;
    }

    return $link;
}

src/reviews/new.php のformのactionを指定する

new.php
<form action="create.php" method="post">

src/reviews/create.php で登録処理を行う。

create.php
<?php

require_once __DIR__ . '/lib/mysqli.php';

function createReview($link, $review)
{
    $sql = <<<EOT
INSERT INTO reviews (
    title,
    author,
    status,
    score,
    summary
) VALUES (
    "{$review['title']}",
    "{$review['author']}",
    "{$review['status']}",
    "{$review['score']}",
    "{$review['summary']}"
)
EOT;
    $result = mysqli_query($link, $sql);
    if (!$result) {
        error_log('Error: fail to create review');
        error_log('Debugging Error: ' . mysqli_error($link));
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $review = [
        'title' => $_POST['title'],
        'author' => $_POST['author'],
        'status' => $_POST['status'],
        'score' => $_POST['score'],
        'summary' => $_POST['summary']
    ];
    // バリデーションする
    $link = dbConnect();
    createReview($link, $review);
    mysqli_close($link);
}

header("Location: index.php");

src/reviews/index.php を一覧ページとして用意する

index.php
<?php
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>読書ログ一覧</title>
</head>

<body>
    <h1>読書ログの一覧</h1>
</body>

</html>

src/reviews/views/new.php を作成し、HTMLをまとめる

new.php
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>読書ログ登録</title>
</head>

<body>
    <h1>読書ログ</h1>
    <form action="create.php" method="post">
        <?php if (count($errors)) : ?>
            <ul>
                <?php foreach ($errors as $error) : ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        <?php endif; ?>

        <div>
            <label for="title">書籍名</label>
            <input type="text" name="title" id="title">
        </div>
        <div>
            <label for="author">著者名</label>
            <input type="text" name="author" id="author">
        </div>
        <div>
            <label>読書状況</label>
            <div>
                <div>
                    <input type="radio" name="status" id="status1" value="未読">
                    <label for="status1">未読</label>
                </div>
                <div>
                    <input type="radio" name="status" id="status2" value="読んでる">
                    <label for="status2">読んでる</label>
                </div>
                <div>
                    <input class="form-check-input" type="radio" name="status" id="status3" value="読了">
                    <label for="status3">読了</label>
                </div>
            </div>
        </div>
        <div>
            <label for="score">評価(5点満点の整数)</label>
            <input type="number" name="score" id="score">
        </div>
        <div>
            <label for="summary">感想</label>
            <textarea type="text" name="summary" id="summary" rows="10"></textarea>
        </div>
        <button type="submit">登録する</button>
    </form>
</body>

</html>

src/reviews/new.php にて、src/reviews/views/new.php を読み込む。また $errors を初期化し、エラーが起こらないようにする

new.php
<?php

$errors = [];

include 'views/new.php';

src/reviews/create.php の末尾にて、src/reviews/views/new.php を読み込む

new.php
<?php

require_once __DIR__ . '/lib/mysqli.php';

function createReview($link, $review)
{
    $sql = <<<EOT
INSERT INTO reviews (
    title,
    author,
    status,
    score,
    summary
) VALUES (
    "{$review['title']}",
    "{$review['author']}",
    "{$review['status']}",
    "{$review['score']}",
    "{$review['summary']}"
)
EOT;
    $result = mysqli_query($link, $sql);
    if (!$result) {
        error_log('Error: fail to create review');
        error_log('Debugging Error: ' . mysqli_error($link));
    }
}

function validate($review)
{
    $errors = [];

    // 書籍名が正しく入力されているかチェック
    if (!strlen($review['title'])) {
        $errors['title'] = '書籍名を入力してください';
    } elseif (strlen($review['title']) > 255) {
        $errors['title'] = '書籍名は255文字以内で入力してください';
    }

    // 著者名が正しく入力されているかチェック
    if (!strlen($review['author'])) {
        $errors['author'] = '著者名を入力してください';
    } elseif (strlen($review['author']) > 255) {
        $errors['author'] = '著者名は255文字以内で入力してください';
    }

    // 読書状況が正しく入力されているかチェック
    if (!in_array($review['status'], ['未読', '読んでる', '読了'])) {
        $errors['status'] = '読書状況は「未読」「読んでる」「読了」のいずれかを入力してください';
    }

    // 評価が正しく入力されているかチェック
    if ($review['score'] < 1 || $review['score'] > 5) {
        $errors['score'] = '評価は1〜5の整数を入力してください';
    }

    // 感想が正しく入力されているかチェック
    if (!strlen($review['summary'])) {
        $errors['summary'] = '感想を入力してください';
    } elseif (strlen($review['summary']) > 10000) {
        $errors['summary'] = '感想は10,000文字以内で入力してください';
    }

    return $errors;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $status = '';
    if (array_key_exists('status', $_POST)) {
        $status = $_POST['status'];
    }

    $review = [
        'title' => $_POST['title'],
        'author' => $_POST['author'],
        'status' => $status,
        'score' => $_POST['score'],
        'summary' => $_POST['summary']
    ];

    $errors = validate($review);

    if (!count($errors)) {
        $link = dbConnect();
        createReview($link, $review);
        mysqli_close($link);
        header("Location: index.php");
    }
}

// ここを追記。以下のHTMLは削除
include 'views/new.php';

src/new.php

new.php
// ここを追記
$review = [
    'title' => '',
    'author' => '',
    'status' => '未読',
    'score' => '',
    'summary' => ''
];

$errors = [];

src/views/new.php

new.php
// 下記をそれぞれ修正
<input type="text" name="title" id="title" value="<?php echo $review['title'] ?>">

<input type="text" name="author" id="author" value="<?php echo $review['author'] ?>">

<input type="radio" name="status" id="status1" value="未読" <?php echo ($review['status'] === '未読') ? 'checked' : ''; ?>>

<input type="radio" name="status" id="status2" value="読んでる" <?php echo ($review['status'] === '読んでる') ? 'checked' : ''; ?>>

<input class="form-check-input" type="radio" name="status" id="status3" value="読了" <?php echo ($review['status'] === '読了') ? 'checked' : ''; ?>>

<input type="number" name="score" id="score" value="<?php echo $review['score'] ?>">

<textarea type="text" name="summary" id="summary" rows="10"><?php echo $review['summary'] ?></textarea>

[登録ページ] CSSでスタイルを整える

目的
Bootstrapを用いて画面のスタイルを整える

必要な知識

  • CSSを書くときの順序
    1, 大枠のレイアウトをスタイリングする
    2, 大枠のレイアウトをレスポンシブ対応する
    3, 個別パーツをスタイリングする
    4, 個別パーツをレスポンシブ対応する

  • 処理を書くコツは先にコメントで実装したい処理を書く

  • エラー処理をしっかり書いておくと、エラーが起きた際の発見が早くなるため返って実装の速度が上がる。サボらずに書こう

  • bootstrapの使い方は以下公式ドキュメント参照
    bootstrap

実践

stylesheets/css ディレクトリを作成した上で、下記コマンドを実行し、SassをCSSにコンパイルする。

linux
vendor/scssphp/scssphp/bin/pscss < stylesheets/scss/app.scss > stylesheets/css/app.css

src/reviews/views/new.php で app.css を読み込む

new.php
<head>
    ...
    <link rel="stylesheet" href="stylesheets/css/app.css">
    <title>会社情報の登録</title>
</head>

src/reviews/views/new.php

new.php
<body>
    <header class="navbar shadow-sm p-3 mb-5 bg-white">
        <h1 class="h2">
            <a class="text-body text-decoration-none" href="index.php">読書ログ</a>
        </h1>
    </header>
    <div class="container">
        <h2 class="h3 text-dark mb-4">読書ログの登録</h2>
        <form action="create.php" method="post">
            <?php if (count($errors)) : ?>
                <ul class="text-danger">
                    <?php foreach ($errors as $error) : ?>
                        <li><?php echo $error; ?></li>
                    <?php endforeach; ?>
                </ul>
            <?php endif; ?>

            <div class="form-group">
                <label for="title">書籍名</label>
                <input type="text" name="title" id="title" class="form-control" value="<?php echo $review['title'] ?>">
            </div>
            <div class="form-group">
                <label for="author">著者名</label>
                <input type="text" name="author" id="author" class="form-control" value="<?php echo $review['author'] ?>">
            </div>
            <div class="form-group">
                <label>読書状況</label>
                <div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="status" id="status1" value="未読" <?php echo ($review['status'] === '未読') ? 'checked' : ''; ?>>
                        <label class="form-check-label" for="status1">未読</label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="status" id="status2" value="読んでる" <?php echo ($review['status'] === '読んでる') ? 'checked' : ''; ?>>
                        <label class="form-check-label" for="status2">読んでる</label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="status" id="status3" value="読了" <?php echo ($review['status'] === '読了') ? 'checked' : ''; ?>>
                        <label class="form-check-label" for="status3">読了</label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <label for="score">評価(5点満点の整数)</label>
                <input type="number" name="score" id="score" class="form-control" value="<?php echo $review['score'] ?>">
            </div>
            <div class="form-group">
                <label for="summary">感想</label>
                <textarea type="text" name="summary" id="summary" class="form-control" rows="10"><?php echo $review['summary'] ?></textarea>
            </div>
            <button type="submit" class="btn btn-primary">登録する</button>
        </form>
    </div>
</body>

[トップページ] HTMLで表示する

目的
読書ログの一覧画面のHTMLを作成する

必要な知識

実践

  • 一覧画面のHTMLを作成する
  • 登録画面と一覧画面のHTMLのレイアウト部分を共通化します
  • バリデーションエラーになった画面(src/create.php)にもスタイルを当ててください。こちらは現状Bootstrapが当たってない状態になっているので、Bootstrapを当てればOKです(講義の会社情報ではこちらの対応やってなく、クイズ独自の対応です)

src/reviews/new.php

new.php
<?php

$review = [
    'title' => '',
    'author' => '',
    'status' => '未読',
    'score' => '',
    'summary' => ''
];
$errors = [];

$title = '読書ログ登録';
$content = __DIR__ . "/views/new.php";
include __DIR__ . '/views/layout.php';

src/review/index.php

index.php
<?php

$title = '読書ログ一覧';
$content = __DIR__ . "/views/index.php";
include __DIR__ . '/views/layout.php';

src/reviews/views/layout.php

layout.php
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="stylesheets/css/app.css">
    <title><?php echo $title; ?></title>
</head>

<body>
    <header class="navbar shadow-sm p-3 mb-5 bg-white">
        <h1 class="h2">
            <a class="text-body text-decoration-none" href="index.php">読書ログ</a>
        </h1>
    </header>
    <div class="container">
        <?php include $content; ?>
    </div>
</body>

src/rviews/views/new.php

new.php
<h2 class="h3 text-dark mb-4">読書ログの登録</h2>
<form action="create.php" method="post">
    <?php if (count($errors)) : ?>
        <ul class="text-danger">
            <?php foreach ($errors as $error) : ?>
                <li><?php echo $error; ?></li>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>

    <div class="form-group">
        <label for="title">書籍名</label>
        <input type="text" name="title" id="title" class="form-control" value="<?php echo $review['title'] ?>">
    </div>
    <div class="form-group">
        <label for="author">著者名</label>
        <input type="text" name="author" id="author" class="form-control" value="<?php echo $review['author'] ?>">
    </div>
    <div class="form-group">
        <label>読書状況</label>
        <div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="status" id="status1" value="未読" <?php echo ($review['status'] === '未読') ? 'checked' : ''; ?>>
                <label class="form-check-label" for="status1">未読</label>
            </div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="status" id="status2" value="読んでる" <?php echo ($review['status'] === '読んでる') ? 'checked' : ''; ?>>
                <label class="form-check-label" for="status2">読んでる</label>
            </div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="status" id="status3" value="読了" <?php echo ($review['status'] === '読了') ? 'checked' : ''; ?>>
                <label class="form-check-label" for="status3">読了</label>
            </div>
        </div>
    </div>
    <div class="form-group">
        <label for="score">評価(5点満点の整数)</label>
        <input type="number" name="score" id="score" class="form-control" value="<?php echo $review['score'] ?>">
    </div>
    <div class="form-group">
        <label for="summary">感想</label>
        <textarea type="text" name="summary" id="summary" class="form-control" rows="10"><?php echo $review['summary'] ?></textarea>
    </div>
    <button type="submit" class="btn btn-primary">登録する</button>
</form>/layout.php';

src/reivews/views/index.php
※sectionタグの中身の文字は何でもOK
※「 」は特殊文字で、スペースを入れるために使用している。本来は「挿入した箇所の前後の改行を禁止する」役割だが、現場ではスペースを入れる目的でもよく使用される

index.php
<?php

$review = [
    'title' => '',
    'author' => '',
    'status' => '未読',
    'score' => '',
    'summary' => ''
];
$errors = [];

$title = '読書ログ登録';
$content = __DIR__ . "/views/new.php";
include __DIR__ . '/views/layout.php';
new.php
<?php

$review = [
    'title' => '',
    'author' => '',
    'status' => '未読',
    'score' => '',
    'summary' => ''
];
$errors = [];

$title = '読書ログ登録';
$content = __DIR__ . "/views/new.php";
include __DIR__ . '/views/layout.php';

src/reviews/create.php
※ここだけファイルの一部を掲載

create.php
$title = '読書ログ登録';
// ここを修正
$content = __DIR__ . "/views/new.php";
include __DIR__ . '/views/layout.php';

[トップページ] 読書ログの一覧を表示する

目的
データベースからデータを取得し、HTMLで画面に表示する

必要な知識

  • データベースからデータを取得
    配列で取得

  • HTMLでデータを表示
    ループで一つずつ表示

  • データがないときの処理
    データがないときなど、イレギュラーパターンを想定して処理を書けることがプロダクションコードへ第一歩

  • TRUNCATE TABLE
    指定したテーブルの全データを削除する

mysql
tbl_name:テーブル名
TRUNCATE TABLE [TABLE] tbl_name
  • 会社情報テーブルの全データを削除する
mysql
TRUNCATE TABLE companies;

PHPでXSS対策するには
htmlspecialchars()を使ってHTML特殊記号をエスケープする

  • HTMLの特殊記号を文字参照に置き換える
$string :変換する文字列
$flags:処理の仕方を指定するフラグ
$encoding:文字を変換する時に使用するエンコーディング
htmlspecialchars($string, $flags, $encoding)
  • ユーザーが入力してDBに保存された以下の値をエスケープするなら
「<script>...</script>」
htmlspecialchars('<script>...</script>', ENT_QUOTES, 'UTF-8')

実行結果

$lt;script&gt;...&lt;/script&gt;

実践

src/revies/lib/escape.php

escape.php
<?php

function escape($string)
{
    return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}

src/reviews/index.php

index.php
<?php

require_once __DIR__ . '/lib/escape.php';
require_once __DIR__ . '/lib/mysqli.php';

function listReviews($link)
{
    $reviews = [];
    $sql = "SELECT id, title, author, status, score, summary FROM reviews";
    $results = mysqli_query($link, $sql);

    while ($review = mysqli_fetch_assoc($results)) {
        $reviews[] = $review;
    }

    mysqli_free_result($results);

    return $reviews;
}

$link = dbConnect();
$reviews = listReviews($link);

$title = '読書ログ一覧';
$content = __DIR__ . "/views/index.php";
include __DIR__ . '/views/layout.php';

src/reviews/views/index.php

index.php
<a href="new.php">読書ログを登録する</a>
<main>
    <?php if (count($reviews) > 0) : ?>
        <?php foreach ($reviews as $review) : ?>
            <section>
                <h2><?php echo escape($review['title']) ?></h2>
                <div>
                    <?php echo escape($review['author']) ?>&nbsp;/&nbsp;
                    <?php echo escape($review['status']) ?>&nbsp;/&nbsp;
                    <?php echo escape($review['score']) ?></div>
                <p>
                    <?php echo nl2br(escape($review['summary']), false) ?>
                </p>
            </section>
        <?php endforeach; ?>
    <?php else : ?>
        <p>まだ読書ログが登録されていません。</p>
    <?php endif; ?>
</main>

[トップページ] CSSでスタイルを整える

目的
一覧画面をデザインする
必要な知識
[登録ページ] CSSでスタイルを整えると同様
実践

src/revies/views/index.php

index.php
<a href="new.php" class="btn btn-primary mb-4">読書ログを登録する</a>
<main>
    <?php if (count($reviews) > 0) : ?>
        <?php foreach ($reviews as $review) : ?>
            <section class="card shadow-sm mb-4">
                <div class="card-body">
                    <h2 class="card-title h4 text-dark mb-3"><?php echo escape($review['title']) ?></h2>
                    <div class="small mb-3">
                        <?php echo escape($review['author']) ?>&nbsp;/&nbsp;
                        <?php echo escape($review['status']) ?>&nbsp;/&nbsp;
                        <?php echo escape($review['score']) ?></div>
                    <p>
                        <?php echo nl2br(escape($review['summary']), false) ?>
                    </p>
                </div>
            </section>
        <?php endforeach; ?>
    <?php else : ?>
        <p>まだ読書ログが登録されていません。</p>
    <?php endif; ?>
</main>

参考にしたサイト

独学エンジニア

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0