5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-03-17

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

#ヘッダーコンポーネントの作成
全てのページで共通で使うヘッダーを作成していきます
Vuetifyで実装しますので、Vuetifyを使用しない方は自作での実装をしてください
まず、client/componentsHeader.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になります
詳しく知りたい方はこちらを参照ください→こちら
これでヘッダーの用意は終わりです

#会員登録の実装
まずはVue側でフォームの実装をしていきます
client/componentsRegister.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になります
ここでは簡単な説明はしますが、もっと詳しく知りたい方は、こちらをご覧ください→こちら

②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);
    }

こちらは、ログアウト時に呼ばれるメソッドになり
ログアウト時にセッションを再生成を行い、レスポンスを返す様にしてあります

上記3つの追加点については、本記事作成でも参考にさせて頂いたこちらの記事を参照してください→こちら

以上で会員登録に必要な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.jsregisterの実装をしていきます

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.jssearchUserアクションを実行しています
これにより、auth.jssearchUserが毎回実行されることで
Laravelで定義した/userにリクエストを投げ現在の認証情報を所得しauth.jssetUserが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

5
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?