Laravel + Vue.jsでクイズを作った

<h1>作ることになったきっかけ</h1>
以前からLaravelのアウトプットとして何か作りたいと思っていたのですが、なかなかネタが思い浮かばずずっと先延ばしになっていました。
そんなある日、趣味の競馬観戦でレースの条件（開催月や距離、競馬場）が自分の記憶のものとかなり変わっていることが度々あり、自分の脳内番組表(競馬の開催スケジュールのこと)のアップデートが必要だと感じました。
そこでLaravelと、ついでにVue.jsも使ってクイズを作ることにしました。

<h1>ざっくり仕様</h1>

* レースはGI、GⅡ、GⅢのようにグレードが定められているので、そのグレードごとにカテゴリー分けして、トップページで挑戦するカテゴリーを選択します。
* 問題は全10問。1つの画面で開催月、距離、競馬場を選択して解答します。
* 「回答する」ボタンをクリックすると、答えと解説が表示されます。
* 「次の問題へ」をクリックすると、次の問題が出題されます。
10問目まで解答が終わると、結果画面が表示されます。

<h1>出題〜解答までの動き</h1>
今回はシンプルに問題の出題のみ行うので、テーブルはproblemsテーブル1つのみです。
せっかくなら画面遷移のないSPAにしたいので、コンポーネントとしてトップページ、出題ページ、結果ページの3つのVueファイルを作成します。
簡単に処理の流れを書くと、
1. fetchItemで問題をテーブルからランダムに1件取得、開発ツールからのカンニングを防ぐため、idとレース名のみ取得。
2. fetchAnswerで該当の問題をidで検索、答え合わせをする。このとき、出題済みの問題がまた出題されないようにprocessed_flgをtrueにします。
3. fetchNewItemで次の問題を取得。

```php:ProblemCpntroller.php
    public function index($category)
    {
        DB::table('problems')->update(['processed_flg' => false]);
        $query = Problem::query();
        $query->where('category', '=', $category);
        $problem['count'] = $query->where('processed_flg', '=', false)->count();

        if($query->exists()){
            $query->select('id', 'title')->get();
            $problem = $query->inRandomOrder()->first();
            return $problem;
        }
    }
    public function fetchNewItem(Request $request)
    {
        $category = $request->input('category');
        $query = Problem::query();
        $query->where('category', '=', $category);
        $query->where('processed_flg', '=', false);

        if($query->exists()){
            $query->select('id', 'title')->get();
            $problem = $query->inRandomOrder()->first();
            return $problem;
        }
    }
    public function fetchAnswer(Request $request)
    {
        $id = $request->input('id');
        $problem = Problem::find($id);
        $problem->processed_flg = true;
        $problem->save();
        return $problem;
    }
```
```php:Problem.vue
methods: {
        async fetchItem() {
            this.is_question = true
            const response = await window.axios.get(`/api/ploblems/category/${this.category}`)

            this.problem = response.data.title
            this.is_answer = false
            this.answer.id = response.data.id
        },
        async fetchNewItem() {
            if(this.counter === 9) {
                this.next_message = '結果発表'
            } else if(this.counter === 10) {
                this.$router.push({ name: 'result', params: { score: this.score } })
            }
            this.errors = {}
            this.answer = {}
            this.counter++
            this.is_question = true
            const response = await window.axios.get('/api/ploblems/category/', {params: {category: this.category}})

            this.problem = response.data.title
            this.is_answer = false
            this.answer.id = response.data.id

        },
        async fetchAnswer() {
            const answer = await window.axios.get('/api/ploblems/', {params: {id: this.answer.id}})
            this.get_answer = answer.data
            this.is_answer = true
            this.is_question = false
            if(this.answer.month !== this.get_answer.month) {
                this.errors.month = true
            }
            if(this.answer.distance !== this.get_answer.distance) {
                this.errors.distance = true
            }
            if(this.answer.place !== this.get_answer.place) {
                this.errors.place = true
            }

            if(!this.errors.month && !this.errors.month && !this.errors.place) {
                this.score++
            }
        }
```
<h1>実際の動き</h1>

![raceq_ .gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442492/8d5a837c-7afb-9ff7-431c-876ba5bb0d87.gif)


<h1>作ってみた感想</h1>
時間の関係で、とにかく動くことを優先してしまったので、リファクタリングの余地ありありになってしまいました。
答え合わせのときの表示も、もっと分かりやすく表示させたかったですね。
でもこうしてアウトプットをすることで、自分の理解の浅い部分を洗い出すことができました。今後も作ってみたい物はあるので、LaravelやVue.jsに限らずいろいろ挑戦したいです！