Laravel+SPA+JTWAuthで認証ありの投稿アプリをつくる 〜パート5〜

# 前提
Laravel5.5
Vagrant環境
認証はJTWAuth使用
SPAはVue、Vue Router
状態管理 Vuex導入

前回のパート1〜4の続き
【 [パート1](https://qiita.com/ProgramingDai/items/403ee4fbc0971827f160) 】【 [パート２](https://qiita.com/ProgramingDai/items/dd14e02b804be1ed5516) 】【 [パート3] (https://qiita.com/ProgramingDai/items/1bd3b21fe97b15d152a0#_reference-94a2c9d5b26f4d09adc8) 】【 [パート4](https://qiita.com/ProgramingDai/items/ee941149a14bdef02fdc) 】

# 今回つくるもの
ログイン・ログアウトの状態に合わせてアラートを表示


# 手順レシピ
1.Vuexをインストール
` $ npm install vuex --save `

2.storeディレクトリ新規作成
resources/assets/js/内に「store」を作成
その中に「index.js」を作成

```javascript:resources/assets/js/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import auth from './modules/auth';
import alert from './modules/alert';
 
Vue.use(Vuex);
 
export default new Vuex.Store({
    modules: {
        auth, alert
    }
});
```

3.モジュールデータ作成
store内に「modules」フォルダを作成し、
「alert.js」「auth.js」を作成

```javascript:resources/assets/js/store/modules/alert.js
const state = {
    message: '', 
    type: 'success'
};
 
const mutations = {
    setAlert (state, {message, type}) {
        state.message = message;
        state.type = type || 'success';
    }
};
 
export default {
    namespaced: true,
    state,
    mutations
};
```

```javascript:resources/assets/js/store/modules/auth.js
import router from '../../router';
 
const state = {
    token: ''
};
 
const mutations = {
    login (state, payload) {
        state.token = payload;
    },
    logout (state) {
        state.token = null;
    }
};
 
const getters = {
    isLogin (state) {
        return state.token ? true : false;
    }
};
 
const actions = {
    login ({ commit }, payload) {
        axios.post('/api/login', {
            email: payload.email,
            password: payload.password
        }).then(res => {
            const token = res.data.access_token;
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
 
            commit('login', token);
            router.push({path: '/'});
            commit('alert/setAlert', { 'message': 'ログインしました。' }, { root: true });
        }).catch(error => {
            commit('alert/setAlert', { 'message': 'ログインに失敗しました。', 'type': 'danger' }, { root: true });
        });
    },
    logout ({ commit }) {
        axios.post('/api/logout').then(res => {
            axios.defaults.headers.common['Authorization'] = '';
            commit('logout');
            router.push({path: '/'});
            commit('alert/setAlert', { 'message': 'ログアウトしました。' }, { root: true });
        }).catch(error => {
            commit('alert/setAlert', { 'message': 'ログアウトに失敗しました。' }, { root: true });
        });
    }
};
 
export default {
    namespaced: true,
    state,
    mutations,
    getters,
    actions
};
```
今後はこちらでログイン・ログアウト処理を行うようにします。

4.router.jsを修正

```javascript:resources/assets/js/router.js
// 中略

// - import store from './store';
// + Vuex
import store from './store/index';

// 中略

router.beforeEach((to, from, next) => {
    // + Vuex
    store.commit('alert/setAlert', { 'message': '' });

    if (to.matched.some(record => record.meta.requiresAuth)) {
        // - if (state.isLogin === false) {
        // + Vuex    
        if (store.getters['auth/isLogin'] === false) {
            next({
                path: '/login',
                query: { redirect: to.fullPath }
            })
        } else {
            next()
        }
    } else {
        next();
    }
});

// 中略
```

5.app.jsを修正

```javascript:resources/assets/js/app.js
require('./bootstrap');

import Vue from 'vue';
// - import store from './store';
// + Vuex
import store from './store/index';
import router from './router';

// - window.state = store.state;

Vue.component('app', require('./components/App.vue'));
Vue.component('navbar', require('./components/Navbar.vue'));

const app = new Vue({
    // + Vuex
    store,
    router
}).$mount('#app');
```

6.App.vueの修正

```javascript:resources/assets/js/components/App.vue
<template>
    <div>
        <!-- + Vuex -->
        <div v-bind:class="'alert alert-' + alertType" v-if="alertMessage">
            {{ alertMessage }}
        </div>

        <!-- 中略 -->

    </div>
</template>

<script>
    // + Vuex
    import { mapActions, mapState } from 'vuex';

    export default {
        // -
        // methods: {
        //     logout() {
        //         axios.post('/api/logout').then(res => {
        //             axios.defaults.headers.common['Authorization'] = '';
        //             state.isLogin = false;
        //             this.$router.push({path: '/login'}); // ログアウト後のルーティング
        //         });
        //     }
        // }
        // + Vuex
        computed: mapState('alert', {
            'alertMessage': 'message',
            'alertType': 'type'
        }),
        methods: mapActions('auth', [
            'logout'
        ]),
    }
</script>
```

7.login.vueの修正

```javascript:resources/assets/js/components/pages/Login.vue
<template>
    <div>
        <!-- - <p v-show="isError">認証に失敗しました。</p> -->

        <!-- 中略 -->

    </div>
</template>

<script>
// + Vuex
import { mapActions } from 'vuex';

export default {
    data () {
        return {
            // - isError: false,
            email: '',
            password: '',
        }
    },
    methods: {
        // -
        // login() {
        //     axios.post('/api/login', {
        //         email: this.email,
        //         password: this.password
        //     }).then(res => {
        //         const token = res.data.access_token;
        //         axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
        //         state.isLogin = true;
        //         this.$router.push({path: '/'}); // ログイン後のルーティング
        //     }).catch(error => {
        //         this.isError = true;
        //     });
        // }

        // + Vuex
        login() {
            this.$store.dispatch('auth/login', {
                email: this.email,
                password: this.password
            });
        }
    }
}
</script>
```

見てわかるように、Vuexでログイン・ログアウト処理を書いているため
ビューファイルのコード量が圧倒的に減り見通しの良いものになっています。

# 参考
https://www.webopixel.net/javascript/1447.html