【PHP】就職活動の為の、webアプリ（ポートフォリオ）設計図公開！｜未経験からエンジニアへ

web系エンジニアを目指し、PHPを学習している初学者です。
就職活動の武器とすべく着手した、webアプリ（ポートフォリオ）作成。
MENTAを利用し、本格的に学習開始してから4ヶ月で、デプロイに漕ぎ着けました！
派手さもモダンな感じもないですが、バリデーションや不正アクセス防止など、地味な部分を丁寧にコーディングしました。
採用担当の方、同じエンジニアを目指す初学者の方、エンジニアを目指そうか迷っている方の目に留まれば幸いです。
#記事の目的
- 面接に備えた補助資料として
- 同じ境遇の方の参考（になれば幸い）

作成した【内定者懇親サイト】について、
1. カタログ
2. 仕様書
3. 気を付けた点
4. 後悔している点
5. （追記）公開後、改善した点
をまとめました。

#1.カタログ
作成したサイトの紹介。
##〜内定者懇親サイト〜
内定承諾してから、入社するまでの期間。
「他の内定者は何人位？年齢は？どんな人？」
「内定式で会う前に知って欲しい・知っておきたい、話のネタを仕込んでおきたい。」
そんな願いを叶える、簡易的な懇親サイト。
###概要
####ユーザー種類
- 管理者
- メンバー

####機能
- 管理者｜メンバー全員の編集、削除。他の管理者の新規登録、編集、削除
- メンバー｜自身の登録、編集、削除。他のメンバー 一覧、詳細の閲覧

####開発環境
MAMPでローカル環境構築。
VScodeでコーディング→Githubでコード管理→herokuでデプロイ

- PHP 7.3.11
- HTML／CSS、Bootstrap4
- MySQL 5.7.26
 
#2.仕様書
サイトの設計図です。
##機能一覧
- メンバーのＣＲＵＤ機能（新規登録、プロフィール編集、削除）
- 管理者のＣＲＵＤ機能
- 管理者権限付与（メンバーの編集、削除）
- メンバー 一覧表示機能
- メンバー 詳細表示機能
- レスポンシブ対応（Bootstrap）

##DB
クリエイト文

- jinjies

```
CREATE TABLE `jinjies` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(100) NOT NULL,
  `password` varchar(255) NOT NULL,
  `last_name` varchar(100) NOT NULL,
  `first_name` varchar(100) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
```
- members

```
CREATE TABLE `members` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(100) NOT NULL,
  `password` varchar(255) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4
```
- members_info

```
CREATE TABLE `members_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `member_id` int(10) NOT NULL,
  `last_name` varchar(100) NOT NULL,
  `first_name` varchar(100) NOT NULL,
  `nick_name` varchar(100) NOT NULL,
  `school` char(10) NOT NULL,
  `prefectures_id` int(10) NOT NULL,
  `message` varchar(240) NOT NULL,
  `icon` varchar(120) DEFAULT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4
```
- prefectures

```
CREATE TABLE `prefectures` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pre_name` char(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8mb4
```
- members_interesting

```
CREATE TABLE `members_interesting` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `member_id` int(11) NOT NULL,
  `interesting1_id` int(11) NOT NULL,
  `interesting2_id` int(11) NOT NULL,
  `interesting3_id` int(11) NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4
```
- interesting

```
CREATE TABLE `interesting` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `intere_name` char(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4
```
##ER図
データベースの設計図。リレーションは慎重に決める。
![NaiteiKonshinDB1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/7394860e-38cb-fd1a-0673-548d3ec51939.png)
##ステートチャート図
![NaiteiKonshin_ステートチャート図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/43d94c4d-b786-604f-feed-8b56f58fce35.png)
##画面遷移図
![NaiteiKonshin_画面遷移図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/059914cb-a081-1b7c-e8ce-75dec44f57c1.png)
これらは[無料で使えるLucidchart](https://www.lucidchart.com/documents#/dashboard?folder_id=home)を利用し、作成しました。
#3.気を付けた点
##基本スキルを示す
- フレームワークを使わない、スクラッチ開発（フロント部分はBootstrap使用）
- CRUD
- 一通りの入力形式（テキストボックス、チェックボックス、ラジオボタン、ファイルのアップロード）実装
- 各項目に対するバリデーションを丁寧に
- パスワードのハッシュ化 （`PASSWORD_DEFAULT`使用）
- 不正アクセス防止

![fraud_access.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/7d5c2778-02b9-b0d4-b99b-47aea2415f76.gif)

##ログイン
1. メンバーログインと新規登録、管理者ログインを１つのログイン画面にまとめ、`login_check.php`にて、移行画面を変更した
2. バリデーションはメソッドを呼び出し、返り値はboolean型とした

```php:login_check.php

class Login
    {
    public function __construct()
    {
        2.validation
        $email_validation = new emailValidation();
        $is_email = $email_validation->isEmail($this->clean['email']);
        //パスワードも同様に
        //validationエラーなら、エラーメッセージを取得し、ログイン画面へ戻る
    }

    public function main()
    {
        1.DB接続し、メンバーテーブル・管理者テーブルの情報と、入力された情報を照合
            メール 一致なし              =>新規登録画面へ
            メール、パスワード
                メンバーと一致           =>メンバーログイン
                管理者と一致            =>管理者ログイン
            メール 一致、パスワード 一致なし =>エラーメッセージを取得し、ログイン画面へ戻る
    }
}
$login = new Login();
$login->main();
```
![login.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/04641c74-151a-60e2-86a2-ce379855e3eb.gif)
##新規登録
1. パスワードの再入力は、ログインユーザーの邪魔にならないよう新規登録画面で行った
![スクリーンショット 2020-02-19 16.13.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/10409f83-0af7-585c-d914-0eb7f43d4a84.png)
2. 再入力が必要な（バリデーションエラー、やり直し選択）時、入力履歴を残し、ユーザー負担を減らした
![スクリーンショット 2020-02-19 16.16.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/ab9eb5b9-9d5c-5b57-0477-07a640dd27cd.png)

3. 入力値を`htmlspecialchars`処理する為に、`hsc.php`にデータを渡すが、配列（３つ選択させるチェックボックス）も同様の処理が出来るようにした

```php:hsc.php
class Hsc
{
    public static function clean($posts)
    {
        foreach ($posts as $post => $value) {
            3.配列の場合
            if(is_array($value)) {
                foreach($value as $key => $val) {
                    $clean[$post][$key] = htmlspecialchars($val, ENT_QUOTES, 'UTF-8');
                }
            } else {
                $clean[$post] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
            }
        }
        return $clean;
    }
}
```

##アラート表示
`$status`で、前画面での処理内容を表示した

```php:list.phpの場合
<?php
    if(!empty($status)):
?>
<div class="alert alert-success" role="alert">
    <?php
        switch ($status) {
            case 'login':
                echo 'ログイン成功しました。';
            break;
            case 'new':
                echo '新規登録が完了しました。';
            break;
            case 'edit':
                echo '編集が完了しました。';
            break;
            case 'delete':
                echo '削除が完了しました。';
            break;
        }
    ?>
</div>
<?php endif; ?>
```
![スクリーンショット 2020-02-19 16.09.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/c4eecdcd-7098-a2e9-b1b8-aa7513a75250.png)

#4.後悔していること
- 開発履歴を、Githubのブランチやissuesを使用して残せば良かった（ずっとmasterにpushし、コメントも「third commit」など無意味なものばかり＾＾；）
- 自身の困りごとを元に、実用性の高いものにすれば良かった
- クラスをあまり活用出来なかった
- Twitter的な交流（コメント、イイね！）がない
- ソートがない
- 変数の付け方、仕様書を定めずに着手してしまった
- 後から「アラート付けよう！」など追加していったので、無駄な修正作業や、抜け・漏れで新たなエラーを生み出してしまった

以上です。
Twitter的な機能やソートについては、追加実装するかもしれませんが、ひとまずはこれでデプロイ。
コーディングして画面が出来上がってからは、夢中で作成することが出来ました。
ぜひ、サイトにアクセスしてみてください！
https://github.com/kunitani920/BullentinBoard

---
#5.（追記）公開後、改善した点
メンターさんを中心に改善案を頂き、一部アップデートしました。
その際、自身で感じた「後悔していること」も一部リカバリー出来たので紹介します。

##ＰＤＯのトランザクション
DBへの新規登録、編集、削除時にトランザクションを追加。

```php:イメージ
<?php

    try {

        //DB接続
        $db = new Db();
        $dbh = $db->dbconnect();

        // トランザクション開始
        $dbh->beginTransaction();

        新規登録、編集、削除処理

        // コミット
        $dbh->commit();
                    
    } catch(PDOException $e) {
        // ロールバック
        $dbh->rollBack();

        // エラーメッセージ出力
        echo $e->getMessage();
        die();
    }
    $dbh = null;
}
```

期待できる効果

- DB同時アクセスによるデータの行き違い防止
- 処理速度の向上

##コンフィグ
コンフィグディレクトリとdatabase.phpファイルを作成し、DB接続情報を移動した。

```php:/confif/database.php
<?php
$db = parse_url($_SERVER['CLEARDB_DATABASE_URL']);
$db['dbname'] = ltrim($db['path'], '/');

return [
    'dsn' => "mysql:host={$db['host']};dbname={$db['dbname']};charset=utf8",
    'user' => $db['user'],
    'password' => $db['pass'],
    'options' => array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>true,
    )
];
?>
```
```php:Db.php
<?php

class Db
{
    public function dbconnect() {
        try {            
            $db = include(__DIR__ . '/config/database.php');
            $dbh = new PDO($db['dsn'], $db['user'], $db['password'], $db['options']);
        } catch(PDOException $e) {
            echo 'DB接続エラー：' . $e->getMessage();
        }
        return $dbh;
    }
}
```

期待できる効果

- 管理者の負担軽減
- 人的ミスの削減

##DB定義とクリエイト文
DB定義を、GithubのREADMEに追加。
DBクリエイト文を、この記事の`2.仕様書`に追加。

##issues、ブランチ活用（4.後悔していることより）
改善項目をissuesに設定。
![スクリーンショット 2020-02-27 19.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/31fcec46-dce7-8f03-3d9c-de98bee3ea2e.png)
![スクリーンショット 2020-02-28 0.01.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/258fc1fe-0481-fd33-1f37-0cb4093ec884.png)

Millestonesで期限設定も体験し、機能を少し把握できた。

修正で間違えても戻れるよう、新しいブランチを切って修正作業。
![スクリーンショット 2020-02-28 0.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/48999ba0-e56e-7f3c-dba7-a8b6060ca28f.png)
ブランチ名がわかりづらく反省。しかし、コミットメッセージはマシになったと思う。

##DB再設計
「１対１関係なのに、なぜテーブルを分けるのか？」
複数の方にご指摘いただいた。

作成当時、
「ID（メール、パスワード）とプロフィール（メッセージなど）を分けて管理したいな」
「３つ選択の趣味、それぞれにIDふるのは変かな？いや、でもそうしないと実現できない」
など考えがまとまらない中
「とりあえず、作ろう！」
と走ってしまった結果です。

ということで、DBの再設計を行った。
![NaiteiKonshinDB1（修正版）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550940/1ec7c89e-1de1-dd8d-6780-ded7f63e3c64.png)
（未反映）