Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3

#前回のパート
前回は、Authの導入とマイグレーションファイルを作成し
今回のTodoアプリで使用するテーブルの作成まで行いました
前回のパートはこちら→[Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2](https://qiita.com/Kyu-Taro/items/8deef5b9be76c091b0cb)
今回は、全てのページで使うヘッダーの作成と認証周りの実装をやっていきます

#ヘッダーコンポーネントの作成
全てのページで共通で使うヘッダーを作成していきます
Vuetifyで実装しますので、Vuetifyを使用しない方は自作での実装をしてください
まず、`client/components`に`Header.vue`を作成し下記を追加してください

```Header.vue
<template>
    <v-card color="grey lighten-4" flat height="200px" tile>
        <v-toolbar dense>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Todo-List</v-toolbar-title>

            <v-spacer></v-spacer>

            <router-link to="/signup">
                <v-btn icon>
                    <v-icon>mdi-account</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/todo">
                <v-btn icon>
                    <v-icon>mdi-align-horizontal-left</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/login">
                <v-btn icon>
                    <v-icon>mdi-login</v-icon>
                </v-btn>
            </router-link>

            <v-btn icon>
                <v-icon>mdi-logout</v-icon>
            </v-btn>
        </v-toolbar>
    </v-card>
</template>
```

templateのみのシンプルな物です
各`<v-btn>`を`<router-link>`で囲み、会員登録やログインやログアウト、Todoページのリンクを設定しています
全てVuetifyになります
詳しく知りたい方はこちらを参照ください→[こちら](https://vuetifyjs.com/ja/components/toolbars/)
これでヘッダーの用意は終わりです

#会員登録の実装
まずはVue側でフォームの実装をしていきます
`client/components`に`Register.vue`を作成してください
`Register.vue`にを下記の様に下記を追加してください

```Register.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">    //・・・①
                <v-text-field v-model="form.name" :rules="nameRules" label="Name" required></v-text-field>    //・・・②
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field>
                <v-btn small>submit</v-btn>
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {    //・・・③
            title: '会員登録',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        data () {
            return {
                form : {
                    name: '',
                    email: '',
                    password: '',
                    password_confirmation: '',
                },
                nameRules: [
                    v => !!v || '名前を入力してください',    //・・・④
                ],
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'    //・・・⑤
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください',
                    v => v.length >= 8 || 'パスワードは8文字以上で入力してください'    //・・・⑥
                ],
                repasswordRules: [
                    v => !!v || 'パスワードの再入力を行ってください',
                    v => v == this.form.password || 'パスワードが一致しません'    //・・・⑦
                ]
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>
```
①ここは全てVuetifyになります
ここでは簡単な説明はしますが、もっと詳しく知りたい方は、こちらをご覧ください→[こちら](https://vuetifyjs.com/ja/components/forms/)

②Vuetifyのフォームになります
`:rules`この部分で下の④⑤⑥⑦のルールとバインディングしており、フロント側でのバリデーションを行っています
`require`は入力必須を意味します
`label`でフォームのラベル名に設定できます
②で設定しているものはどれも同じ要領です
次に設定しているバリデーションのルールについて軽く触れておきます
④入力の値が存在しているかどうか(未入力でないか)をチェックしています
⑤正規表現でEmailの形式かどうかをチェックしています
⑥パスワードが8文字以上で入力されているかをチェックしています
⑦passwordの入力値を一致しているかをチェックしています

③part1でインストールしたVue Metaです
ここでは、titleを【会員登録】としてlangをjaに設定しています
Vue Metaをこの様な設定を行うために使用します

これで、Vuetifyを使って簡単にフォームを用意できバリデーションまで行えました
次に会員登録機能の実装を行っていきます

#会員登録機能の実装(Laravel側)
まずは、`routes/api.php`を開きルートを定義していきます
`/register`は会員登録用のルート
`/user`はログインをしているのかを常時チェックするために用意したルートになります(この後のVue側の実装で再度説明します)

```api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/register', 'Auth\RegisterController@register')->name('register');    //追加
Route::get('/user', function(){    //追加
    return Auth::user();
});
```

次に`app/Http/Controllers/Auth/RegisterCOntroller.php`を開いて下記を追加します

```RegisterController.php
    protected function registered(Request $request, $user)    //追加
    {
        return $user;
    }
```

`registerd()`メソッドをオーバーライドすることで
登録処理をした際にUser情報を返却できる様に設定できます
この値を使って今後認証を行うので、上記を追加しました

このついでに
ログイン時にとログアウト時にも必要になる処理を先にやっておきます
`app/Http/Controllers/Auth/LoginController.php`を開いて下記を追加してください

```LoginController.php
    protected function authenticated(Request $request, $user)    //追加
    {
        return $user;
    }
```

こちらの追加の内容も、`registerd()`メソッドのオーバーライドをした際と同じ理由になり
ログイン時にUserの情報を返却する様に設定しています

次に`LoginController.php`でさらに`loggedOut()`メソッドを追加してください

```LoginController.php
    protected function loggedOut(Request $request)
    {
        $request->session()->regenerate();
        return response('', 200);
    }
```
こちらは、ログアウト時に呼ばれるメソッドになり
ログアウト時にセッションを再生成を行い、レスポンスを返す様にしてあります

上記３つの追加点については、本記事作成でも参考にさせて頂いたこちらの記事を参照してください→[こちら](https://www.hypertextcandy.com/vue-laravel-tutorial-authentication/)

以上で会員登録に必要なLaravel側の処理の実装は終わりです(ログイン時とログアウト時の設定も行いました)

#会員登録処理(Vue側)
まず`Register.vue`に下記を追加してください

```Register.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">
                <v-text-field v-model="form.name" :rules="nameRules" :counter="10" label="Name" required></v-text-field>
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field>
                <v-btn small @click="register">submit</v-btn>    //追加
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {
            title: '会員登録',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        created () {
            const user = this.$store.getters['auth/user'];
            if (user !== null) {
                this.$router.push('/todo');
            }
        },
        data () {
            return {
                form : {
                    name: '',
                    email: '',
                    password: '',
                    password_confirmation: '',
                },
                nameRules: [
                    v => !!v || '名前を入力してください',
                ],
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください',
                    v => v.length >= 8 || 'パスワードは8文字以上で入力してください'
                ],
                repasswordRules: [
                    v => !!v || 'パスワードの再入力を行ってください',
                v => v == this.form.password || 'パスワードが一致しません'
                ]
            }
        },
        methods: {    //追加
            async register() {
                await this.$store.dispatch('auth/register', this.form);
                this.$router.push('/todo');
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>
```

①クリックイベントを追加しています
クリックすることで`register`メソッドを呼び出します

②このメソッドによって登録処理を実行します
厳密に言うと、登録処理をするアクションを呼び出しています(auth.jsについてはこの後実装します)
会員登録の処理後に、Todoページに遷移する様にしています

次に、`register()`でdispatchしている`auth.js`の`register`の実装をしていきます

```auth.js
import axios from 'axios';

const state = {
    user: null    //追加
}

const getters = {
    user: state => state.user    //追加
}

const mutations = {
    setUser (state, user) {    //追加
        state.user = user;
    }
}

const actions = {
    async register({ commit }, data) {    //追加
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    },

    async searchUser({ commit }) {    //追加
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null
        commit('setUser', user);
    },

}

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}
```

```main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';
import store from './store/store';
import vuetify from './plugins/vuetify';
import axios from 'axios';
import VueMeta from 'vue-meta';

Vue.config.productionTip = false

Vue.use(axios);
Vue.use(VueMeta, { refreshOnceOnNavigation: true });

const createApp = async () => {    //追加
    await store.dispatch('auth/searchUser');

    new Vue({
        router,
        vuetify,
        store,
        render: h => h(App)
    }).$mount('#app')
}

createApp();    //追加
```

一気にauth.jsの追加とmain.jsの一部追加を行いました
一つづ説明していきます

```auth.js
const state = {
    user: null  
}
```
stateでuserの情報を管理します
このアプリケーション内では、全てこのuserの値の有無を見てログイン認証やユーザー情報の所得を行います

```auth.js
const getters = {
    user: state => state.user
}
```
stateのuserの情報を所得するgetterを定義しています

```auth.js
const mutations = {
    setUser (state, user) {
        state.user = user;
    }
}
```
受け取ったデータをstateのuserに値をセットするミューテーションを定義しています

```auth.js
    async register({ commit }, data) {    //追加
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    }
```
Laravel側の`api.php`で定義した`register`に対してpostしています
第二引数のdataはフォームに入力された値がv-modelでバインドされたdataの中身が渡されています
ここでの返り値として、先ほどオーバーライドした`registered()`メソッドでの$userの値がresponseにセットされます
その値をsetUserに対してcommitしてstateのuserに値を設定しています

```auth.js
    async searchtUser({ commit }) {    //追加
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null;
        commit('setUser', user);
    }
```
Laravel側の実装の際に`api.php`にて定義した`/user`のルートに対してアクセスをしています
`/user`のルートはAuth::user()として認証しているユーザーの情報を返却する様に作りました
Auth::user()は認証していればユーザー情報が、認証されていなければnullが返ってきます
それを利用して、上記アクションでは`/api/user`に対してリクエストをしてその戻り値(認証されていればユーザー情報、認証されていなければnull)を
変数responseに入れています
`const user = response.data ? response.data : null;`で認証されていてresponseに値が入っていればその値を
認証されておらずnullが入っていればnullを変数userに入れています
そのuserをsetUserでcommitしてstateのuserに値を入れています
なのでstateのuserはログインしていればユーザー情報、ログインしていなければnullが常に入っている様にしています
あとはこれを常時実行するためにどうしているかと言うと、先ほど追加した`main.js`の追加部分になります

```main.js
const createApp = async () => {
    await store.dispatch('auth/searchUser');

    new Vue({
        router,
        vuetify,
        store,
        render: h => h(App)
    }).$mount('#app')
}

createApp();
```

ここでVueインスタンスが作られる際に毎回、`createApp()`を実行することにより
`auth.js`の`searchUser`アクションを実行しています
これにより、`auth.js`の`searchUser`が毎回実行されることで
Laravelで定義した`/user`にリクエストを投げ現在の認証情報を所得し`auth.js`の`setUser`がcommitされ
stateのuserに値がセットされると言うことになります

```Register.vue
created () {
    const user = this.$store.getters['auth/user'];
    if (user !== null) {
        this.$router.push('/todo');
    }
}
```
この部分でそのページにアクセスする度に`created()`が実行され
`auth.js`のgettersを利用してstateの現在のuser情報を所得し
ログインしていれば`/todo`のページへ遷移する様に設定しています(ログインしていない場合はログインページが表示される)

ここまでの実装で
会員登録が行える様になりますので、フォームから入力して登録を実行してみてください
正常であれば、入力内容がDBに保存され登録処理ができます

#終わりに
今回はヘッダーのコンポーネントの作成と、会員登録機能の実装を一通り行いました
次回は、今回登録した内容を利用してログインができる様に、ログイン機能の実装をやっていきます

次のパートはこちら→[Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4](https://qiita.com/Kyu-Taro/items/b9fadb7ea7dc6f1aadbe)