初めてのチーム開発で Best Award を受賞した話

# 目次
[1.はじめに](#1-はじめに)
[2.メンバー構成や役割について](#2-メンバー構成や役割について)
[3.チーム開発 week が始まるまでに行ったこと](#3-チーム開発weekが始まるまでに行ったこと)
[4.追加設定](#4-追加設定)
[5.ガントチャートによる進捗管理](#5-ガントチャートによる進捗管理)
[6.タスク管理機能の実装](#6-タスク管理機能の実装)
[7.デイリポ　機能の紹介](#7-デイリポ　機能の紹介)
[8.振り返り](#8-振り返り)

# 1. はじめに
私は2023年10月より、内定直結型エンジニア学習プログラム「アプレンティス」に2期生として参加しています。

[【働きながら学べる】エンジニア実習 アプレンティス](https://apprentice.jp/)

その中で初めて取り組んだチーム開発が先日発表会を迎え、なんと、プロのエンジニアの方々が選ぶ **Best Award を受賞**することができました！！

一連の開発過程と、完成したアプリケーションをご紹介しつつ、良かった点、反省点を振り返り、学びを整理しようと思います。

チーム開発未経験の駆け出しエンジニアの方が、チーム開発のイメージを掴む助けになれば幸いです。
長文になっているので、目次を参考に、必要な箇所を読んでいただけたらと思います。

# 2. メンバー構成や役割について
私のチームは3人で、
- プログラミング歴3年程、テスト業務経験のある方
- プログラミング歴４ヶ月程、実務未経験の方
- プログラミング歴1年程、実務未経験の私

で、３人ともチーム開発は初めての体験でした。

その中で私はプロジェクトの進捗管理などを行う **プロジェクトリーダー** と、発表会ではチーム代表で**発表者**を務めさせていただきました。

# 3. チーム開発weekが始まるまでに行ったこと
チーム開発にだけに集中して取り組むことができるのは、1週間と決まっていました。
それまでの期間にミーティングを重ねて、チーム開発 week が始まったらすぐにコーティングをスタートできるように準備をしました。

私が初めに行ったこと
- やるべきことを洗い出してスケジューリングし、チームメンバーに提案
- Notion のチーム専用ページを用意し、チーム開発のルールや参考資料、チームミーティングの記録などができるように整理
- 開発スケジュールを共有できるよう、Notion でガントチャートを準備

Notion をご存知ない方がいましたら、ぜひ使ってみることをオススメします！
Notion を導入したことで、チームミーティングの記録や参考資料、これからご紹介する一連の開発過程などを簡単に整理・共有することができました。

ちなみに、Notion にはコードを記述できる機能もあり、まさにエンジニア向けのツールと言えます。私は普段からエンジニア関連の学習内容を Notion でまとめています。

### 課題定義
**「自分たちの役に立つものを作ろう」**
このお題に対して、
- 自分たち = アプレンティス生
- 役に立つもの = 困っていることや、不便に感じていることを解決するもの

と置き換えて考えました。

そこで私が思いついたのが、PDCAを回すためのタスク管理アプリでした。

現状として、
- アプレンティス生は毎日、学習に関する日報をdiscordに投稿することになっている
- その内容を X(旧Twitter)に投稿することも推奨されている
- 提出Questの締切を厳守するために、タスク管理が重要だと言われている

という点に対し、以下の課題がありました。

- 日報を自己成長に活かしきれていない (ただ投稿すればいいのではなく、PDCAを回して自分の成長に繋げていくことが重要)
- 提出Questの締切でいつも慌てる
- discord にも X にも投稿するという毎日のルーティンにひと手間かかる
- X の文字数制限により、場合によっては内容を分割して何件も投稿する必要がある

これらの課題を解決するアプリケーションを作ったらどうかと提案し、チームメンバーにも賛同してもらえたため、そのようなタスク管理アプリを制作することに決定しました。

### 要件定義
課題に対して、どのような状態になれば解決したと言えるのかを考えました。

- その日のタスクの達成率を確認できる
- 達成率を元に毎日振り返りができる
- タスク管理と振り返りの情報を、そのまま日報として discord や X に手軽にシェアできる
- シェアの方法として画像のダウンロードにも対応し、文字数制限の壁を超える

### インセプションデッキ
アジャイル開発で用いられるインセプションデッキも取り入れました。

参考:[【導入必須】スクラム開発におけるインセプションデッキの作成方法・具体例](https://do-scrum.com/inception-deck/)

各問いに対しての私たちの回答をまとめたので、詳しくは下記をご覧下さい。

<details><summary>詳しくはこちら</summary>

#### 問1.私たちはなぜここにいるのか
タスクの進捗を可視化して管理することで、課題の納期を守りながら、自己学習のPDCAを回していきたいから

#### 問2.エレベーターピッチ
効率的なタスク管理をしたいアプレンティス生向けのプロダクトです。
これは、タスクの進捗管理、日報の各種SNSへのシェアをすることができ、一般のタスク管理アプリや日報アプリとは違ってこれらの作業をまとめて管理し、可視化することがができます。

#### 問3.パッケージデザイン
２～３色くらいのシンプルなデザイン

#### 問4.やらないことリスト
やること：
- タスクを登録
- 実績を登録
- 達成率を図表で可視化
- 振り返りメモ
- discord,Xへのシェア機能(画像、テキスト出力)

やらないこと：
- タスクのカテゴリー分け

状況を見て決める：
- ドラッグでのタスクの並び替え
- ChatGPTによるフィードバック
- スマホ版のレスポンシブ
- 日報提出忘れ防止の通知機能
- ログイン機能

#### 問5.ご近所さんを探せ
- チームメンバー
- 同期
- メンター

#### 問6.技術的な解決策を描く
言語：HTML, CSS, JS, PHP, SQL（フレームワークなし）
ライブラリ:TwiiterAPI,jsPDF,PHP WebDriver
ツール：Git/GitHub, WSL2, Docker, MySQL, デザインツール（AdobeXD, Figma）,Discord, X

#### 問7.夜も眠れない問題
期限内に終わらない
実装方法が分からない

#### 問8.期限を見極める
12/1（金）までに完成を目指す

発表会は12/3(日)なので、遅くとも前日には形になっている必要があり、余裕を持って金曜までに完成を目指そうということになりました。

#### 問9.トレードオフスライダー

|  | 1 | 2 | 3 | 4 | 5 |
| --- | --- | --- | --- | --- | --- |
| 機能を全部揃える（スコープ遵守） |  |  | 〇 |  |  |
| 予算内に収める（予算遵守） |  |  |  |  |  |
| 期日を死守する（納期遵守） |  |  |  |  | 〇 |
| バグを出さない（品質遵守） |  |  |  | 〇 |  |

何よりも期限を遵守することを最優先に置きました。

#### 問10.何がどれだけ必要か
スキル：今まで学習してきたところ
期間：12/2（土）まで

私たちがこれまで学習してきたHTML,CSS,JavaScript,PHP,SQLの知識を駆使して作る必要がありました。
期限は決まっているので、その期限内で完成させることが必須です。

</details>

以上のインセプションデッキを話し合うことで、開発のイメージを具体化し、スコープや優先順位の認識の共有を図りました。

### 設計
PDCAのサイクルに添って、以下の機能を実装しようと話し合いました。

- カレンダーとタスク登録(P)
カレンダーをクリックするとその日のタスク一覧が表示され、タスクの登録・削除ができる
(PHP, JavaScript, SQLで実装。DBとしてMySQLを使用)

- タスクの完了ステータスの切り替え(D)
チェックボックスをクリックすることで、タスクの完了・未完了のステータスを切り替えできる
(使用技術・ツールは同上)

- 完了ステータスの円グラフ化(C)
完了ステータスから達成率を算出し、円グラフとして表示する
(JavaScript, 円グラフ化のAPI使用)

- 振り返りメモ(C)
1日の振り返りをメモとして残せる
(PHP, JavaScript, SQLで実装。DBとしてMySQLを使用)

- 1日の実績のシェア機能(C)
テキストでのコピー、画像出力、Xへの投稿ボタンをそれぞれ実装する
(PHP, JavaScript, SQLで実装。DBとしてMySQLを使用。XのAPI使用)

- ChatGPTからのフィードバック(C)
振り返りメモに対して、ChatGPTからコメントが返ってくるようにする
(PHP, JavaScript, SQLで実装。DBとしてMySQLを使用。ChatGPTのAPI使用)

### 記述アーキテクチャ
使用技術は以下の通りです。
![tech_architecture_diagram](https://github.com/OBookBook/APPRENTICE-dev1-team-dev/assets/130152109/c4817646-c2c0-4794-9034-0c67a6cd1a4c)

### ワイヤーフレーム
Adobe XD を使えるのがチーム内で私だけだったため、ワイヤーフレームのたたき台を作成させていただき、ミーティングで画面共有しながら完成させました。

ログイン画面や会員登録画面を除いて、メインの画面は使いやすさを考慮し一画面で完結するようにしようということになりました。

[Adobe XD によるワイヤーフレームはこちら](https://xd.adobe.com/view/e3aeb51c-bea7-46f9-aa28-353943c2dbe1-b6fd/screen/4684894a-c3e6-44fc-b0c3-f0dec44f2974/)

### ロゴマークの作成
Canva を使用して、チームメンバー3人で話し合いながら、ロゴマークを作成しました。
アプリ名は、daily report の略で「デイリポ」と名付けました。

カラーは、discordでアプレンティスのアイコンに使われている色をベースにした、優しめの色にしました。

また、日報やメモのイメージから、手書き風のペンの画像を採用しました。

![dairy_report_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/aed4481d-2949-3ac4-fe85-e69cbb781e3a.png)


### テーブル定義
テーブル定義については、案を持ち寄って話し合った結果、他のチームメンバーの案を採用することになりました。

![er_diagram](https://github.com/OBookBook/APPRENTICE-dev1-team-dev/assets/130152109/d0094a49-a21b-4c29-bd51-58b3ea72080d)

この時点ではまだ JavaScript や AJAX の学習期間の前だったため、どのようにデータをやりとりするかのイメージが私の中でまだハッキリしていませんでした。
(チーム開発 week の直前が JavaScript の学習期間でした)

### デザイン
Adobe製品やFigmaを使用できるのがチーム内で私だけだったため、デザイン案のたたき台を作成させていただき、このデザインで問題ないかチームメンバーに確認をしました。
結果的に、特に変更や修正もなく、そのまま採用になりました。

作成する際に意識したことは、以下の通りです。
- ロゴマークの色をメインカラーとし、色相環でトライアド（トライアル）の関係にある色をピックアップして、ヘッダー・フッターや背景、アクセントカラーとして取り入れた
- 大事な情報が目に入るように、色は大きくわけて3色程度にし、ごちゃごちゃせず優しい色味にした
- カレンダー、タスク一覧、実績の3つの領域がパッと見て、わかりやすいようにした

[Figma によるデザインはこちら](https://www.figma.com/file/sRAsZrJGoqSqxSVdqdv3hG/%E3%83%87%E3%82%A4%E3%83%AA%E3%83%9D?type=design&node-id=2%3A4&mode=design&t=Tun4ptqUwXQEVGFl-1)

### タスクばらし・担当決め
私がデザインを作成している間に、別のメンバーがタスクばらしをしてくれました。

開発手法として、
- フロントエンドとバックエンドで担当を分ける方法
- 機能ごとに担当を分ける方法

があり、それぞれがバランスよく学びを深めるために機能毎に担当を分ける方法が推奨されていたため、その方法を採用しました。

大きな括りとして、
- カレンダー表示機能
- タスク管理機能
- 実績表示・シェア機能

があり、私はその中のタスク登録機能と、併せてヘッダー・フッターのコーティングを担当することになりました。

### 環境構築
私がデザインを担当している間に、チームメンバーが Docker で開発環境を整えてくれました。

PHP & Apache, MySQL のコンテナを作成し、PHP Code Sniffer で静的解析ができるようになっています。
ChatGPT や X の API に関するライブラリや設定もされています。

ここまでの内容を計画的に準備を進めて、チーム開発 week を迎えました。

# 4. 追加設定
チーム開発 week が始まり、実際にコードを書き始めてから気づいた点がいくつかあり、Docker 環境に関して私の方で追記をして、下記の内容も導入しました。
- Xdebug と Remote Development を導入して PHP のデバッグができるようにした
> Xdebug参考資料：[PHPの開発効率化！VSCodeでDockerコンテナに入って、Xdebugでデバッグする設定＆使用方法](https://qiita.com/ryamate/items/74c57a290b78c812f089)
- PHP Code Snifer を簡略化したコマンドで実行できるようにし、併せてカラーの追加や、何の規約に違反しているかが表示される設定にした
> PHP Code Sniffer 参考資料：[【PHP】PHP_CodeSnifferを使う - 初期セットアップとかんたんな使い方 -](https://www.s-watanabe.work/2019/11/phpcodesniffer.html)
- dot env のライブラリを導入して .env ファイルを作成して環境変数を設定し、 docker-Compose.yml や PHP のコードに MySQL のパスワードなどをベタ書きしなくて済むようにした
> dot env 参考資料：[DockerでMySQLサーバーを構築するときの環境変数の設定](https://qiita.com/pachi-dragon/items/8d248c518eb25cd9cf68)
- PHP で MySQL に接続する際に PDO が使えるように、ライブラリをインストールした
> PDO 参考資料：[PDOの利用](https://www.javadrive.jp/php/pdo/)

# 5. ガントチャートによる進捗管理
前述した Notion のガントチャートを活用し、チームメンバーの進捗管理を行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/e78fa894-f008-e3ef-3f0b-4378a682e994.png)

各メンバーが自分の担当機能についてさらにタスクばらしをして、各タスクのステータスを進行中、完了、未着手とすることで、上のガントチャートの達成率がパーセントで表示されるようになっています。
私が挙げたタスクは以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/733b9583-cbea-1004-4d57-32632299590b.png)

ガントチャートは、以下のテンプレートを利用しました。
Notionのテンプレート：[Gantt Chart](https://www.notion.so/ja-jp/templates/gantt-chart)

# 6. タスク管理機能の実装
まず初めにヘッダーとフッターの実装をパパッと終わらせてから、メインのタスク管理機能の実装に着手しました。

## グローバル変数 $_POST で受け取る方法
初めは、フォームから送信された値を PHP 側の $_POST で受け取り、PDO を使って MySQL に接続してデータを挿入する方法でタスクを登録しました。

タスク一覧を表示する際には、タスク名だけでなくデータベースに登録された時に割り振られるタスク固有のidも取得し、完了・未完了のチェックボックスと削除ボタンのそれぞれと併せて設置した hidden の input 要素に value としてその id を持たせました。

そうすることで、タスクの削除や完了・未完け了のステータス変更かフォームで送信された際に、タスクの id で該当のタスクをデータベースから検索することができるようにしました。
<details><summary>実際のコードを抜粋 (リファクタリング前)</summary>

index.php
```php
<?php
require_once(__DIR__ . '/header.php');
require_once(__DIR__ . '/footer.php');
require_once(__DIR__ . '/../functions/connectSql.php');
require_once(__DIR__ . '/../functions/ShowTasks.php');

getHeader();
?>
<main>
  <section id="task-management">
    <h2><span>11月20日のタスク一覧</span></h2>
    <ul class="task-lists">
      <?php
      /* この時点では、ログイン機能、カレンダー機能が未実装だったため、
        ユーザーは id=1、日付は'2023-11-20'で固定 */
      $userId = 1;
      $date = '2023-11-20';
      showTasks($userId, $date);
      ?>
      <li class="task-list">
        <form class="add_task_form" action="../functions/TaskControl.php" method="post">
          <input type="hidden" name="form_id" value="input_task">
          <input class="input_task" type="text" name="input_task">
          <button class="add_task_btn" type="submit">＋</button>
        </form>
      </li>
    </ul>
  </section>
</main>
<?php
getFooter();
```
 
 　
登録されているタスク一覧を表示するファイル
```php
<?php

require_once(__DIR__ . '/../functions/connectSql.php');

function getTasksOfTheDay($userId, $date)
{
  // MySQL に接続する
  $dbh = connectSql();

  $query = <<<EOT
  SELECT *
    FROM tasks
  WHERE user_id=:user_id
    AND execution_date=:date
  ORDER BY display_order
  EOT;

  $stmt = $dbh->prepare($query);
  $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
  $stmt->bindValue(':date', $date, PDO::PARAM_STR);
  $stmt->execute();

  $tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);

  $dbh = null;

  return $tasks;
}

function showTasks($userId, $date)
{
  $tasks = getTasksOfTheDay($userId, $date);

  foreach ($tasks as $task) {
?>
    <li class="task-list">
      <div>
        <form method="POST" action="../functions/TaskControl.php">
          <input type="hidden" name="form_id" value="get_task">
          <?php if ($task["completion_status"] === 0) : ?>
          <!-- ここに task_id の値を仕込んだ -->
            <input class="checkbox" type="checkbox" name="<?php echo $task["task_id"]; ?>">
          <?php else : ?>
          <!-- ここに task_id の値を仕込んだ -->
            <input class="checkbox" type="checkbox" name="<?php echo $task["task_id"]; ?>" checked>
          <?php endif; ?>
          <?php echo $task["task_name"]; ?>
        </form>
      </div>
      <form method="POST" action="../functions/TaskControl.php">
        <input type="text" name="form_id" value="delete_task" hidden>
        <!-- ここに task_id の値を仕込んだ -->
        <input type="text" name="delete_task_id" value="<?php echo $task["task_id"]; ?>" hidden>
        <label>
          <span class="delete-icon material-symbols-outlined">delete</span>
          <button type="submit" name="delete-btn" hidden>
        </label>
      </form>
    </li>
<?php
  }
}
```
　

タスク登録、削除に関するファイル
```php
<?php

require_once(__DIR__ . '/../functions/connectSql.php');

// フォームから送信された新規タスクを取得し、保存する
function getInputTask($userId, $date)
{
  $inputTask = $_POST['input_task'];

  // MySQL に接続
  $dbh = connectSql();

  $query = <<<EOT
  INSERT INTO tasks
    (user_id,
    task_name,
    execution_date,
    completion_status,
    display_order)
  VALUES
    (:userId,
    :task,
    :date,
    0,
    (SELECT (MAX(display_order) + 1)
      FROM tasks as t
    WHERE t.user_id = :userId))
EOT;

  $stmt = $dbh->prepare($query);
  $stmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $stmt->bindParam(':date', $date, PDO::PARAM_STR);
  $stmt->bindParam(':task', $inputTask, PDO::PARAM_STR);
  $stmt->execute();

  $tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);

  $dbh = null;

  return $tasks;

  echo $inputTask;
}


function deleteTask()
{
  $taskId = $_POST['delete_task_id'];

  $dbh = connectSql();

  $query = <<<EOT
      DELETE FROM tasks
      WHERE task_id = :taskId
  EOT;

  $stmt = $dbh->prepare($query);
  $stmt->bindParam(':taskId', $taskId, PDO::PARAM_INT);
  $stmt->execute();

  $dbh = null;
}

// POST送信されたときに、送られてきた form_id の値により分岐
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  if (isset($_POST['form_id'])) {
  
    if ($_POST['form_id'] === 'delete_task') {
      deleteTask();
      
    } elseif ($_POST['form_id'] === 'input_task') {
        /* この時点では、ログイン機能、カレンダー機能が未実装だったため、
        ユーザーは id=1、日付は'2023-11-20'で固定 */
      getInputTask(1, '2023-11-20');
    }
  }
}

header("Location: ../pages/task-management.php");
```
</details>

## AJAX を利用した動的な実装

一通り実装ができてから、「できれば AJAX を利用してページをリロードせずにタスクの登録・削除・完了ステータスの変更ができたらいいね！」ということになりました。
フォームをPOSTで送信する方法だと、ページのリロードが起こってしまいます。
JavaScript で AJAX を利用すれば、そのようなリロードなしに、快適に操作することができるのです。

参考資料：[Ajaxとはどういうもの？基本情報から仕組みまで詳しく解](https://hnavi.co.jp/knowledge/blog/ajax/)

チーム開発の直前の週が JavaScript の学習期間だったのですが、AJAX は「発展内容」として学習必須項目とはなっておらず、私は AJAX まで学習できていませんでした。
そのため、学びながらではありましたが、Axios という JavaScript のライブラリを用いて、AJAX で動的にページを操作する方法にリファクタリングをし始めました。

参考資料：[利用ガイド：Axiosの使い方を完全解説](https://apidog.com/jp/blog/axios-introduction/#:~:text=%E3%81%A8%E6%80%9D%E3%81%84%E3%81%BE%E3%81%99%E3%80%82-,Axios%E3%81%A8%E3%81%AF,%E3%81%97%E3%82%84%E3%81%99%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)

カレンダーの日付がクリックされたら、というイベントリスナーを設置して、クリックされたカレンダーの日付を取得するところまでは、カレンダー担当のチームメンバーが実現してくれました。

私はそこから以下の流れで実装していきました。

- クリックされた日付の情報を AJAX 通信で PHP に渡す
- PHP 側で該当の日付のタスク一覧情報をデータベースから取得
- 取得したデータを JavaScript に返す
- データを元に JavaScript で DOM を操作し、HTML を生成する

詳しいコードについては、下記をご覧下さい。

<details><summary>コードを一部抜粋</summary>

index.html のタスク一覧の部分
```html
<section id="task-management">
      <h2><span>タスク一覧</span></h2>
      <ul class="task-lists">
      <!-- この部分に JavaScript で生成した HTML が埋め込まれる -->
      </ul>
    </section>
```
　
カレンダー関係の JavaScript ファイル
```javascript
//(略)

// タスク一覧とレポートの表示。カレンダーの日付がクリックされた際に呼び出す
showContents(date, month, day) {
    const UL_OF_TASK_LIST = document.querySelector(".task-lists");
    UL_OF_TASK_LIST.innerHTML = "";
    let userId = 1;
    
    // Axios を使って AJAX 通信で PHP ファイルとやり取りをする
    return axios
      .post("http://localhost:9080/src/php/functions/ExistingTask.php", {
        userId: userId,
        execution_date: date,
      })
      .then((response) => {
        // タスク一覧情報と、「本日の実績」欄に表示する情報が返ってくる
        let taskList = response.data.taskList;
        let report = response.data.report;
    
        // ここで下の関数を呼び出している
        this.createList(taskList, month, day, date);
        this.createReport(report, taskList);
      })
      .catch((error) => {
        console.log(error);
      });
}
  
// クリックされたカレンダーの日付を渡して、タスク一覧のHTML生成
createList(taskList, month, day, date) {
    const UL_OF_TASK_LIST = document.querySelector(".task-lists");
    const LIST_TITLE = UL_OF_TASK_LIST.previousElementSibling.children[0];
    
    LIST_TITLE.innerText = `${month}月${day}日のタスク一覧`;
    
    if (taskList.length) {
      // 登録されているリストの表示
      for (let i = 0; i < taskList.length; i++) {
        UL_OF_TASK_LIST.innerHTML += `
      <li class="task-list">
        <form class="checkbox_form">
          <input class="checkbox" type="checkbox" name="${taskList[i].task_id}">
        </form>
        <div class="task_name">
          ${taskList[i].task_name}
        </div>
        <form class="delete_form">
          <input type="text" name="delete_task_id" value="${taskList[i].task_id}" hidden>
          <label>
            <span class="delete-icon material-symbols-outlined">delete</span>
            <button type="submit" name="delete-btn" hidden></button>
          </label>
        </form>
      </li>`;

    if (taskList[i]["completion_status"] === 1) {
      let checkboxes = document.querySelectorAll(".checkbox");
      let lastCheckbox = checkboxes[checkboxes.length - 1];
      lastCheckbox.setAttribute("checked", true);
    }
  }
}

// タスク追加欄の表示
UL_OF_TASK_LIST.innerHTML += `
  <li class="task-list list_to_add_task">
  <form class="add_task_form">
    <input type="hidden" name="form_id" value="input_task">
    <input class="input_task" type="text" name="input_task" maxlength="255" required>
    <button class="add_task_btn disabled" type="submit" disabled><span class="add_task_btn_inner">＋</span></button>
  </form>
</li>
<div class="feed_back"></div>`;

//(略)
```
　
リクエストが送られた先の PHP ファイル
```php
<?php

require_once("Task.php");

class ExistingTask extends Task
{
  public function checkReceivedInfo($rawData)
  {
    if (!empty($rawData)) {
    　　　　// json 形式のデータを PHP の配列形式に変換
      $jsonData = json_decode($rawData, true);

      if (array_key_exists('execution_date', $jsonData)) {

        $this->getTaskList($jsonData);

        exit;
      } else {

        $response = [
          "error" => "error1:データがありません",
        ];
        echo json_encode($response);
        exit;
      }
    } else {

      $response = [
        "error" => "error2:データがありません",
      ];
      echo json_encode($response);
      exit;
    }
  }

  // タスク一覧の取得
  public function getTaskList($jsonData)
  {
    $userId = $jsonData["userId"];
    $date = $jsonData["execution_date"];

    // MySQL へ接続
    $dbh = $this->connectionToSql->connectSql();

　　// ユーザーIDと日付から、その日のタスク一覧を取得
    $query = <<<EOT
  SELECT *
    FROM tasks
  WHERE user_id=:user_id
    AND execution_date=:date
  ORDER BY display_order
  EOT;

    $stmt = $dbh->prepare($query);
    $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
    $stmt->bindValue(':date', $date, PDO::PARAM_STR);
    $stmt->execute();

    $taskList = $stmt->fetchAll(PDO::FETCH_ASSOC);

　　//　「本日の実績」表示用のデータを併せて取得
    $query = <<<EOT
  SELECT *
    FROM reports
  WHERE user_id=:user_id
    AND submitted_date=:date
  EOT;

    $stmt2 = $dbh->prepare($query);
    $stmt2->bindValue(':user_id', $userId, PDO::PARAM_INT);
    $stmt2->bindValue(':date', $date, PDO::PARAM_STR);
    $stmt2->execute();

    $report = $stmt2->fetch(PDO::FETCH_ASSOC);

    $dbh = null;

    $response = [
      "error" => "OK",
      "taskList" => $taskList,
      "report" => $report,
    ];

　　// json 形式に変換して、JavaScript にデータを返す
    echo json_encode($response);
  }
}

// JavaScript から送られてきた json データの取得
$rawData = file_get_contents("php://input");

$existingTask = new ExistingTask();

$existingTask->checkReceivedInfo($rawData);
```

</details>


### 難しかった点
AJAX 通信に変更したことで特に難しかった点は、 PHP にリクエストを送信する際に、うまくデータが返ってこないことでした。
console.log で確認すると、Axios のレスポンスでは HTTP ステータスは 200 （通信成功）になっているにも関わらず、返ってくるデータは "" （空）だったり、逆に ページ全体の DOM 要素が返ってくるといった具合です。
PHP での SQL 文に問題があるわけでもなく、原因が分からず格闘していました。

結論として、json.encode のコードの後で
```
header("Location: phpファイル名");
```
としてページをリロードしていたことがどうやら原因の1つようだというのが私の現時点での理解です。
まだまだ、AJAX や async await など、通信の部分について学んでいく必要があると感じています。

タスクの登録や削除、完了ステータスの切り替えについても、タスク一覧の表示と同じような方法を用いて、JavaScript と PHP を連携して実装ができました。


# 7. デイリポ　機能の紹介
出来上がったアプリ デイリポ の機能をご紹介します。

ページを開くと、カレンダーには本日の日付がハイライトされ、下部には本日の日付のタスク一覧と、その達成率などが表示されます。
カレンダーには、その日のタスクの完了率が円グラフで表示されます。
日付をクリックすると、その日付に対応したタスク一覧と実績が下部に表示されます。
![スクリーンショット 2023-12-08 050919.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/38a69cc6-11bd-8201-4ba0-dd43f2118c57.png)

 
左下のタスク一覧の欄では、新しいタスクの登録、タスクの削除、完了のチェックを入れることができます。(この部分を担当しました)

![スクリーンショット 2023-12-08 050607.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/5e867921-92d0-5181-79d2-43484c04f601.png)

 
右下の欄には、タスクの完了・未完了が反映されている他、振り返りメモ入力欄と、入力したメモに対する ChatGPT のコメントが表示されます。（ChatGPTの性能の関係で、画像では英語になってしまっています）

 ![スクリーンショット 2023-12-08 051930.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070527/6e4d8d0c-1501-e90b-a825-dbb44e9cf90a.png)

**作品のリポジトリはこちら**
GitHub： [デイリポ -daily report-](https://github.com/OBookBook/APPRENTICE-dev1-team-dev)



# 8. 振り返り
今回、初めてのチーム開発を経て、多くの学びがありました。
全体を通して良かった点、今後の課題を整理し、次のアクションに繋げようと思います。

:::note info
良かった点
- Notion でチーム開発の情報整理・共有をしたことで、情報のやりとりや振り返りがスムーズになった
- 事前のミーティングで開発のスコープや優先順位など認識の共有を図ったことで、迷いなく始められた
- ガントチャートでの進捗管理を取り入れたことで、全体の進捗とメンバーのタスクをひと目で確認できた
- データベースやDocker環境などの事前準備ができていたため、スムーズに開発をスタートできた
- 職業訓練で学んだ Web デザインの知識と技術を活かすことが出来た
- デザインカンプをしっかり作って共有していたことで、アプリの完成系のイメージができた
- ターゲットや課題を明確にしたことで、目指す方向性が定まった
- 他のチームメンバーが担当している機能と連携する時に、機能間のデータの受け渡しの方法やデータの形式を相談し、上手く連携することができた
- 他のメンバーより早く自分の担当機能を実装できたため、進捗が遅れているメンバーに声をかけて、タスクを巻きとることができた
- GitHub リポジトリへのプッシュのタイミングやコンフリクトの解消について、ある程度感覚を掴むことが出来た
:::


:::note warn
今後の課題
- 自分が担当している部分以外が今どんな状態なのかなかなか見えてこなかったので、もっと密に情報共有をする
- 最後の2日くらいは、PCに向かっている間は常に voice chat をマイクオフで繋げておいて気軽に声を掛け合えるようにしたが、もっと早くそうしていればよかった
- 事前のミーティングで開発のスコープや優先順位など認識の共有を図ったのはよかったが、認識の齟齬がないか適宜確認するべきだった
- ガントチャートでの各メンバーのタスクばらしについて、もっと細かくタスクをばらすとともに、優先順に並べてもらうと今後の予定や進捗がもっと把握しやすかった
- チームメンバー間でプログラミング歴や知識・技術に差があったため、メンバーが何をしようとしているのか分からない部分があったので、事前にもっと詳しく説明してもらえばよかった
- リリースできるような状態になったのがギリギリだったため、最低限の機能でのリリースをまずは目指し、そこから機能を追加したりアップグレードする方がよかった
- 開発期間の中間地点に評価日を設けて、中間地点までにどのくらいできているべきかを事前に決めておき、軌道修正をできるようにしたらよかった
- 難しい技術を使うことより、課題を明確にし、課題に沿ったコンセプトを掲げ、そのコンセプト通りのプロダクトを作って、課題を解決出来ているかを意識する必要がある
- 開発期間までに実装方法などを調べきれていなかったため、事前に調べておくともっと効率よく開発できた
- 病気などチームメンバーに思わぬ個人的なトラブルが発生したため、事前に余裕を持ったスケジューリングをしておくことと、トラブル発生後の進捗をもっと細かく確認すべきだった
- ギリギリまでコードを書いていて、発表の練習がほとんど出来なかったため、次回は発表用のスライドももっと早い段階から準備し、発表練習にも時間を取るようにする
:::



これからチーム開発に携わる方は、参考にしていただけたら幸いです。

# 9. さいごに
今回は初めてのチーム開発ということで、反省点もたくさんあります。
ですが、チームメンバーと協力して1週間でなんとかアプリを作り上げ、Best Award を受賞できたことは、なによりも大きな励みになりました。

足を引っ張るわけにはいけないと思い、毎日長時間作業し、最終日には２０時間を超える時間を費やして必死にエラーと戦いました。
結果的に、チームメンバーが各々担当したカレンダー機能、タスク管理機能、実績表示・シェア機能それぞれについて、メンターからお褒めの言葉をいただくことができ、初めてのチーム開発としてそれなりの成果を出せたのではないかと思います。

また、プロジェクトリーダーと発表者という役割も進んで引き受けたことで、個人的にもより多くの学びを得ることができました。

来週には次のチーム開発のメンバーが発表されます。
今回の学びを大いに活かして、さらによいチーム開発にできるようにしたいです。
今は Laravelとテスト駆動開発の学習期間なので、チーム開発weekが始まるまでの間に知識・技術をさらに高められるように、これまでの復習と併せて学びを深めていこうと思います。

ここまでお読みいただいてありがとうございました。
少しでも参考にしていただけましたら幸いです。