
今回の記事では、作成したポートフォリオについて、まとめていきたいと思います。
下記が、今回、ご紹介するポートフォリオ「Quizopia」のサイトURLです。

「Quizopia」作成の経緯
プログラミングスクール「ウェブカツ」では、たくさん手を動かし、自ら考え、アウトプットしていくスタイルのスクールのため、毎月のように、何かを作りながら学習を進めていました。このアプリを作る段階では、入部後9ヶ月が経過しており、Laravel部まで終了していた状態でした。JS上級部でReactも学習済みだったため、LaravelとReactの2つを組み合わせたアウトプットを作りたいと考えていたところ、小1の娘が最近、クイズにハマっており、普段から、「クイズ出して~、クイズ出して~」と頻繁に言うので、クイズアプリを作って喜ばせてあげようと思ったのがきっかけです。
TypeScriptもせっかくなので学習しておこう♫
せっかくなので、未学習のTypeScriptについても触っておこうと思い、TypeScriptの使用も決めました。使用した感想は、型定義は最初は手間を感じましたが、コードが増えるにつれて、コードが読みやすくなり、その恩恵を実感することができ、すぐに慣れて使用することができました。
作成手順①:Udemy教材の活用

まず、初めにUdemy教材
~【Laravel11】クイズアプリを作りながら自力でアプリ開発する力を身に着けよう【要件定義・設計~プログラミングまで~
を取り組み、アプリの骨組みを作成しました。この講座は、要件定義・設計の流れを丁寧に解説してくださっており、draw.ioの使い方やマインドマップの使い方、アプリでコードを書き始める前に、どのようなことを考え、準備しておくべきかの説明が具体的にされており、また説明も分かりやすく聞きやすいため、非常にお勧めの講座です。

講義を進めるときに意識したポイント
Udemyの講義をそのまま模写して真似しているだけでは、頭をあまり使わないため、
バックエンドでは、Laravel11を使用して、
フロントエンドでは、Reactを使って、
講義を進めていきました。
その際、Inertia.jsを使用することになるため、Inertia.jsのドキュメントも読みつつ、開発を進めるにつれて、英語のドキュメントに対しての抵抗感も薄れていきました。
Udemy教材終了時の実装内容
こちらの講義を終了した時点で実装が完了したのが、以下の機能です。
- 管理者として登録・ログインすると、4択クイズの問題の作成者になれる
→ただし、ログイン認証において、メール認証機能などはない状態(Laravelのデフォルト状態) - クイズはカテゴリーを作成し、各カテゴリーに関するクイズを登録することで、クイズの解答者(未ログイン者でもOK)は、クイズを解くことができる
- UIには、TAILBLOCKSを使用
作成手順②:オリジナル機能を搭載する
娘と話し合い、どんな機能がほしいかを聞いたところ、1番最初に言われたのが、「ポケモンなき声クイズ」がほしい!とのことでした。聞いたときには、「PokeAPIうまく使えば、できそうだな」という感じでした。他にも、話を聞いて、一緒に情報を整理していくと以下の機能を追加実装することに決めました。
- プレイヤーログイン機能(プレイヤー名・パスワードのみ登録)
- プレイヤーレベルアップ機能(アバター・経験値・レベル表示)
- プレイヤークイズ復習機能(プレイヤーのみ可能)
- プレイヤーランキング機能
- ポケモンランダムなき声クイズ機能
- クイズ作成者の登録・ログイン方法にGoogle認証機能
- クイズ作成者の登録でのEmail認証機能
そして、UI部分は、TAILBLOCKSでは、ありきたりな見栄えになってしまうので、使用したことがないUIコンポーネントを使ってみようと思い、shadcn/uiを使うことを決めました。
shadcn/uiを使用しての感想
コンポーネントを完全にプロジェクトにインストールして使用することができるため、アレンジがしやすく、また、直感的で使いやすく、オシャレなUIがすぐに作成できるため、とても気に入っています。ドキュメントも分かりやすかったです。ただ、日本語訳されたドキュメントは情報が古かったため、うまくインストールされないこともあったりと、調べ方の良い経験にもなりました。
トップ画面


プレイヤーログイン機能
プレイヤーとして登録する場合は、娘が登録して使うため、プレイヤー名(重複は不可)とパスワードだけをDBに登録して簡易にログインできるようにしました。
よって、まずサイトを訪れた際のヘッダー部分は、以下のようになります。

プレイヤー登録ボタンを押すと以下の画面に移動します。

プレイヤーログインボタンを押すと以下の画面に移動します。

プレイヤーレベルアップ機能(アバター・経験値・レベル表示)
プレイヤーとして、ログインすると、ヘッダーが以下のように変わります。

以下のようにプレイヤー情報カードのようなものを作成しました。けいけんちは、クイズを1問解くと1追加されていき、10ごとにレベルが上がっていく分かりやすいレベルアップ方式にしました。

アバターの画像の部分をクリックすると、以下のような画面が表示され、画像の変更が可能です。

プレイヤークイズ復習機能(プレイヤーのみ可能)
プレイヤーとしてログインしてクイズを解くメリットとして、間違えた問題を復習できる機能を追加しました。また、クイズを解く問題数も選択できる機能がプレイヤーにはあります。
以下がプレイヤーとしてのクイズを解く前の画面です。

これに対して、プレイヤーとしてログインせずにクイズを解く前の画面が以下になります。

クイズスタートボタンを押すと、、以下のような画面になり、チェックボックスで正解を選びます。

※問題文の右側のサウンドマークを押すと、問題文を読んでくれます。
※英語だと、ネイティブな発音で読んでくれます。

上の画像が正解したときの画面で、下の画像が不正解のときの画面です。

プレイヤーとしてログインした状態で、すべて問題を解き終わると、以下のような画面になります。
正解数だけでなく、正解率も表示してくれます。

これに対して、ログインせずに問題をすべて解き終わると、少しさびしい感じがします笑。

ちなみに、10問正解して、レベルがあがったときは、下記のような画面が出現します。

そして、レベルが高いプレイヤーベスト3がランキング表に乗るようになっています。

クイズ作成者機能紹介

ヘッダーの右側に「クイズ作成者登録」・「クイズ作成者ログイン」ボタンがあります。
クイズ作成者登録画面

このサイトのテーマカラーは、黄色・紫としたので、こちらでは紫を上手に使ってみました。
Googleアカウントで登録ボタンを押せば、Googlleアカウントでの登録も可能です。
新規登録時には、メール認証が必要で、以下のようなメールが届きます。

上記の、このボタンをクリックを押すと登録が完了して、以下のクイズ作成者画面に移動します。

カテゴリー新規登録ボタンを押すと、クイズのカテゴリーを新規登録することが可能です。

登録したカテゴリーに対して、クイズを作成していきます。
カテゴリーの詳細ボタンを押すと以下の画面に移動します。

上記の画面でクイズの新規追加・編集・削除を行うことが可能です。
カテゴリー編集ボタンやクイズを追加ボタンを押すと、以下の画面に移動します。


ユーザーが使いやすいように、左上に戻るボタンと右下に登録ボタンを配置しました。
地味ですが、ユーザーの使いやすさに関しては、今後もこだわっていきたいと思います。
ポケモンなきごえクイズ機能紹介
その名の通り、ポケモンのなきごえクイズがランダムで生成され、なきごえを聞いて、4択から正解を選ぶクイズです。この機能が1番、苦労した部分です。
なきごえクイズで苦労した点
まず、初期の実装方法としては、PokeAPIからそのまま音声ファイルをAxiosを使って取得して、Laravelのコントローラーを使ってクイズを作成して、クイズと音声を流すという実装にしていました。
この実装方法でも問題なく、クイズは作成できた(上記の動画)のですが、問題点が2つありました。
①iPhoneやiPadなどのiOS環境では音声が出力されない
②本番環境でクイズをたくさん使用した場合、PokeAPIの制限がかかる可能性がある
特に、①のiOS端末で音声が流れない問題は解決するのに時間がかかり、大変な思いをしましたが、今となっては良い思い出です。
①iPhoneやiPadなどのiOS環境では音声が出力されないの解決方法
howler.jsを入れてみたり、色々と試しましたがうまくいかずで、解決に丸1日かかりました。
解決方法として、m4a形式のなきごえ音声ファイルをサーバーに用意して使用することで解決することができました。最初は、mp3形式のなきごえ音声ファイルを使っていたのですが、iOSとmp3は相性が悪いということを知ることができました。ちなみに、このときにスマホやタブレットでchrome://inspectを使えば、コンソールを見ることができることを覚えました。
②クイズをたくさん使用した場合、PokeAPIの制限がかかる可能性の解決方法
PokeAPIからなき声のmp3形式のなきごえ音声データをダウンロードし、
次に、Apowersoft( https://www.apowersoft.jp/free-online-video-converter )を使用して、m4a形式に変換を行い、こちらをサーバーに保存して使用する形の実装に切り替えました。さすがに、約1000匹分のポケモンのなきごえはいらないと思ったので、赤緑と金銀世代のポケモンである251匹分のなきごえデータをダウンロードすることに決めました。
ダウンロードは手動ではなく、プログラミングによる自動処理で感動!
手動で251匹分のなきごえ音声ファイルをダウンロードするのは非常に手間がかかるため、プログラミングを用いて、ダウンロードを試みました。以下のコードを実行してみて、ダウンロードが自動で進んでいく様子を見たときは、プログラミングを学習していて良かったと嬉しく思いました(^^)
以下、使用した自動ダウンロード用のphpファイル
<?php
require __DIR__ . '/../vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
// 保存先ディレクトリの設定
$saveDir = __DIR__ . '/../public/audio/pokemon_cries/';
// ディレクトリが存在しない場合は作成
if (!file_exists($saveDir)) {
mkdir($saveDir, 0755, true);
}
// HTTPクライアントの初期化
$client = new Client();
// ダウンロード済みのファイル数をカウント
$downloadedCount = 0;
// 進捗表示用の関数
function showProgress($current, $total) {
$percent = floor(($current / $total) * 100);
echo sprintf("\rダウンロード進捗: %d/%d (%d%%)", $current, $total, $percent);
}
echo "ポケモンの鳴き声のダウンロードを開始します...\n";
// ダウンロードする開始ID、終了IDを設定
$startId = 152;
$endId = 251;
$total = $endId - $startId + 1;
// $startIdから$endIdまでのポケモンの鳴き声をダウンロード
for ($id = $startId; $id <= $endId; $id++) {
try {
// PokeAPIからポケモンデータを取得
$response = $client->get("https://pokeapi.co/api/v2/pokemon/{$id}");
$pokemonData = json_decode($response->getBody(), true);
// 鳴き声のURLを取得
$cryUrl = $pokemonData['cries']['latest'];
// ファイル名を設定(3桁の数字にパディング)
$fileName = sprintf("%03d.mp3", $id);
$savePath = $saveDir . $fileName;
// 鳴き声データをダウンロード
$client->get($cryUrl, ['sink' => $savePath]);
$downloadedCount++;
showProgress($downloadedCount, $total);
// APIの制限を考慮して待機(1秒)
sleep(1);
} catch (GuzzleException $e) {
echo "\nエラー(ポケモンID: {$id}): " . $e->getMessage() . "\n";
// エラー時は少し長めに待機(3秒)
sleep(3);
}
}
echo "\n\nダウンロードが完了しました!\n";
echo "保存先ディレクトリ: " . $saveDir . "\n"
echo "ダウンロードされたファイル数: " . $downloadedCount . "\n";
echo "\n注意: ファイルはMP3形式でダウンロードされています。M4A形式に変換する必要があります。\n";
家族の感想と今後に向けて
アプリが完成したので、娘と嫁にアプリを実際に使ってもらったのですが、娘は大喜び!!
夢中になって、ポケモンなきごえクイズをやってくれていました。嫁も同世代でポケモン好きなので、夜1人でなきごえクイズをやっているところを見たときに、微笑ましく思いました(^^)笑
ちなみに、最初に作ったときは、赤緑だけの151匹のクイズだったのですが、どうしても金銀まで入れてほしいという嫁のリクエストにより、251匹まで増えました笑。
自分が作ったアプリで、誰かが笑ったり、喜んだりする姿を見れるのは嬉しいです。
引き続きスキルを磨き、作りたいものが作れるエンジニア、人の役にたてるものが作れるエンジニアになれるように精進していこうと思います!
このブログを読んでくださった方も、Quizopiaをぜひ触ってみてもらえたら幸いです。

ここまで読んでいただき、ありがとうございました(^^)
補足:コード説明パート(時間があるときに更新)
自分の作ったコードを他人に説明できることは大切だと思うので、主要パートのコードの解説を、今後、別記事にしていきます。