【ララはじ #8】 Laravel9とVue3でだいたいやりたいこと出来ました。
まぁ、ログイン認証ができて、ページの認証ガードが出来ればいいんじゃないかと思ってたので、最後に、雛形になるところまでやっていきます。あとなんですか、途中で気がついたんですが、今後、Composition API というのを使った記述になるっぽいので、そんな感じで修正します。
まず、認証用にストアを作ります。resources/js/stores/auth.js というファイルにします。
import { ref } from 'vue';
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', () => {
const authenticated = ref(false);
const user = ref({});
function login() {
axios.get('/api/user').then(({data}) => {
this.user = data;
this.authenticated = true;
this.router.push('/');
}).catch((e) => {
this.user = {};
this.authenticated = false;
})
}
function logout() {
axios.post('/api/logout').then((res) => {
this.user = {};
this.authenticated = false;
this.router.push('/login');
});
}
return { authenticated, user, login, logout };
});
ここで、Routerをつかって、画面遷移させていますので、piniaの初期化でRouterが使える様、resources/js/app.js に追記します。
import './bootstrap';
import { createApp, markRaw } from "vue";
(中略)
const pinia = createPinia();
pinia.use(({ store }) => {
store.router = markRaw(router);
});
const vuetify = createVuetify();
(中略)
まぁ公式サイトに載ってますw
次にルートに認証を追加していきます。ファイルはresources/js/router/index.js ですね。
import Login from '@/components/pages/Login.vue';
import Default from '@/components/layouts/Default.vue';
import Dashboard from '@/components/pages/Dashboard.vue';
import UserList from '@/components/pages/UserList.vue';
import NotFound from '@/components/pages/NotFound.vue';
import { useAuthStore } from '@/stores/auth';
const routes = [
{
name: "login",
path: "/login",
component: Login,
meta: {
middleware: "guest",
},
},
{
path: "/",
component: Default,
meta: {
middleware: "auth",
},
children:[
{
name: "dashboard",
path: "",
component: Dashboard,
},
{
name: "userlist",
path: "users",
component: UserList,
},
],
},
{
path: '/:catchAll(.*)',
component: NotFound,
},
];
const router = createRouter({
routes,
history: createWebHistory(),
});
router.beforeEach((to, from, next) => {
const auth = useAuthStore();
if (to.meta.middleware === "guest") {
if (auth.authenticated) {
next({ name: "dashboard" })
} else {
next()
}
} else {
if (auth.authenticated) {
next()
} else {
next({ name: "login" })
}
}
});
export default router;
ちょっと、ページ増えましたが、以前のダッシュボード画面をベースレイアウト(resources/js/components/layouts/Default.vue)になるように、ネスト構成で、新しくダッシュボード(resources/js/components/pages/Dashboard.vue)と一覧表(resources/js/components/pages/UserList.vue)を作成し、存在しないページを指定した場合の画面(resources/js/components/pages/NotFound.vue)を作成しました。ソースは以下になります。
# Default.vue
<template>
<v-app>
<Header></Header>
<Sidebar></Sidebar>
<v-main>
<router-view></router-view>
</v-main>
<Footer></Footer>
</v-app>
</template>
<script setup>
import Header from '@/components/layouts/Header.vue';
import Footer from '@/components/layouts/Footer.vue';
import Sidebar from '@/components/layouts/Sidebar.vue';
</script>
# Dashboard.vue
<template>
<v-card class="ma-4">
<v-card-title>
<h1>Laravel9、さわり始めました。</h1>
</v-card-title>
</v-card>
</template>
# UserList.vue
<template>
<v-container>
<v-row no-gutters>
<v-col cols="12">
<v-card class="ma-1">
<v-card-title>
<h3>一覧表</h3>
</v-card-title>
<v-card-text class="mb-0">
<v-table>
<thead>
<tr>
<th class="text-left">
ID
</th>
<th class="text-left">
ユーザ名
</th>
<th class="text-left">
メールアドレス
</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in items"
:key="item.id"
>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.email }}</td>
</tr>
</tbody>
</v-table>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const items = ref([])
async function getUsers() {
const res = await axios.get('/api/users')
items.value = res.data;
}
onMounted(() => {
getUsers()
});
</script>
# NotFound.vue
<template>
<h1>Oops!</h1>
</template>
あと、ヘッダーにログアウト機能を追加します。ファイルは、resources/js/components/layouts/Header.vue です。
<template>
<v-app-bar app>
<v-app-bar-nav-icon @click="layout.toggle">
</v-app-bar-nav-icon>
<v-toolbar-title>
お試しシステム
</v-toolbar-title>
<v-menu width="200px">
<template v-slot:activator="{ props }" v-slot:append>
<v-btn icon v-bind="props">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list
color="white"
>
<v-list-item @click="auth.logout" role="button">
<template v-slot:prepend>
<v-icon icon="mdi-account"></v-icon>
</template>
<v-list-item-title>ログアウト</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
</template>
<script setup>
import { useLayoutStore } from '@/stores/layout'
import { useAuthStore } from '@/stores/auth'
const layout = useLayoutStore();
const auth = useAuthStore();
</script>
サイドバー(resources/js/components/layouts/Sidebar.vue)も変更します。
<template>
<v-navigation-drawer v-model="layout.isOpenDrawer" app elevation="4">
<v-list dense nav>
<v-list-item
v-for="item in items"
:key="item.title"
:to="item.to"
>
<template v-slot:prepend>
<v-icon :icon="item.icon"></v-icon>
</template>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</template>
<script setup>
import { useLayoutStore } from '@/stores/layout'
import { ref } from 'vue';
const layout = useLayoutStore()
const items = ref([
{
title: "ダッシュボード",
icon: "mdi-view-dashboard",
to: "/"
},
{
title: "一覧表",
icon: "mdi-table-column-width",
to: "/users"
}
])
</script>
あとは一覧表を取得するapiを作成して、Laravel側のrouteに登録すれば、なんとなくできあがりですね。
今回、ソースプログラム多めで、記事としてはどうかと思いますが、
デザイン面と不完全なエラー処理はアレとしても、雛形としては使えそうな気がしてきました。
ちょっと目を離した隙に、世界は色々と変わっていく今日この頃、
以前は出来ていたものを、こういうやり方で再現しつつ学習していくのはいかがでしょうか。