20210411のvue.jsに関する記事は10件です。

【Vue.js】createdとmountedの違い

createdとmountedの違い 前提 ライフサイクルというものが何なのか理解するのが最初は非常に難しいらしい 利用頻度の高いcreated, mountedで何が行われるのかと2つの区別がはっきりつけばライフサイクルの理解が進むかも 結論 created:インスタンス初期化時、DOMが生成される前 mounted:インスタンス初期化時、DOMが生成された後 ライフサイクルフックとは vue.jsの初期化の中の決められたタイミングで実行される関数 created, mountedもライフサイクルフックの中の2つ 各ライフサイクルフックの中にプログラムを記述することでvue.jsの初期化の流れの中で記述したプログラムを必ず実行させることができる 本題 elの中身を知る elはvue.jsインスタンスがマウントを行う要素で、elを指定した要素の中でだけvue.jsを動かすことができる vueコンポーネント <div id="app"> <h1>Hello World</h1> <button v-on:click="showEl">show el</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data: { }, methods:{ showEl : function(){ console.log(this.$el) //this.$elをでelの中身が確かめられる } } }) </script> elの中身 = elはnew Vueインスタンスで指定した要素そのもの <div id="app"> <h1>Hello World</h1> <button v-on:click="showEl">show el</button> </div> createdとmountedの違い createdとmountedの違いは下記のように説明されている createdはDOMがまだ作られていない状態で、mountedではDOMが作成された直後の状態である 以下で確認 new Vue({ el: '#app', data: { }, created : function(){ console.log('created') console.log(this.$el) }, mounted : function(){ console.log('mounted') console.log(this.$el) } }) createdではDOMが作成されていないので、this.$elはundefinedとなっており、mountedではDOMの作成が完成していることが確認できた よって createdではDOMの取得ができない mountedではDOMの取得ができ流ので、ここからDOM操作が行える createdでは何が完了しているのか 今度はthisを使ってVueインスタンスそのものを確認する el: '#app', data: { message : 'Hello World' }, created : function(){ this.message = 'Hello Vue' console.log('created') console.log(this) } dataオブジェクトがリアクティブになっているので、 createdの中でデータプロパテにデータを挿入することは可能 createdとbeforeCreateの違い beforeCreateでは、dataの中身を確認することができますが、createdのようにmessageの上書きを行うことはできない beforeCreateでインスタンスの作成が完了、dataオブジェクトがリアクティブになっていないという説明を受けても理解することができる 理解が深まったこと elの理解 beforeCreate, created, mountedのライフサイクルフックの違い 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】Vue Routerのナビゲーションガードでリダイレクトの設定

Vue Routerのナビゲーションガードでリダイレクトの設定 投げ銭アプリを作成中。 ログインしていないユーザーはログイン画面にリダイレクトされるようにしたい 公式 router.beforeEach ルーターインスタンスのメソッドで、画面遷移前に実行される処理を引数に記載 遷移先をnext()で指定 router.beforeResolve 主にガードする場所は以下の4箇所 1. グローバルbeforeEach 2. ルート単位beforeEnter 3. コンポーネント単位beforeRouteEnter 4. コンポーネント単位beforeRouteUpdate 実際設定したコードは以下 router.beforeEach((to, from, next) => { const requiresAuth = to.matched.some(record => record.meta.requiresAuth) // ルートメタフィールドのrequiredAuthプロパティがtrueの場合、認証されているかどうかをチェックする挙動を実装 if (requiresAuth) { //true or false firebase.auth().onAuthStateChanged(function (user) { // ログインしているユーザーを取得(ユーザの有無) // ログインされているかどうか認証 if (user) { next() //nextを呼ぶとtoのコンポーネントに遷移 // ログインページにリダイレクト } else { next({ path: '/signin', query: { redirect: to.fullPath } }) } }) // メタフィールドがture出なければダッシュボードに遷移してしまう } else { next() } }) 参考 ルートメタフィールドのrequiredAuthプロパティがtrueの場合、認証されているかどうかをチェックする挙動を実装する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue Routerの書き方、使い方

Vue Routerの準備(Vue-CLI) Router をmain.jsに登録 main.js import Vue from 'vue' import App from './App.vue' import router from './router' import store from "./store" Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app') router/index.js にVueコンポーネントのルートを登録する 以下のようにcliを介してvueとvue-routerをimport Vue.use(VueRouter)でVue Routerが使えるようになる Vueコンポーネントのルートもここに登録 router/index.js import Vue from 'vue' import VueRouter from 'vue-router' import BasicForm from '@/components/BasicForm.vue' import QuestionnaireForm from '@/components/QuestionnaireForm.vue' import ConsultationFrom from '@/components/ConsultationFrom.vue' import ConfirmationFrom from '@/components/ConfirmationFrom.vue' Vue.use(VueRouter) const routes = [ //ルートをいくつか定義 { //各ルートは 1 つのコンポーネントとマッピングされる必要がある(ここでは、path: '/') path: '/', //path: '宛先パス' name: 'BasicForm', //(name: コンポーネント名) component: BasicForm //conponent コンポーネント名 }, { path: '/questionnaire', name: 'QuestionnaireForm', component: QuestionnaireForm // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. // component: () => import(/* webpackChunkName: "about" */ '../views/QuestionnaireForm.vue')//これは何 }, { path: '/consultation', name: 'ConsultationFrom', component: ConsultationFrom }, { path: '/confirmation', name: 'ConfirmationFrom', component: ConfirmationFrom } ] const router = new VueRouter({ //ルーターインスタンスを作成して、ルートオプションを渡す mode: 'history', base: process.env.BASE_URL, routes // `routes: routes` の短縮表記 }) export default router 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】Vue Routerの書き方、使い方

Vue Routerの準備(Vue-CLI) Router をmain.jsに登録 main.js import Vue from 'vue' import App from './App.vue' import router from './router' import store from "./store" Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app') router/index.js にVueコンポーネントのルートを登録する 以下のようにcliを介してvueとvue-routerをimport Vue.use(VueRouter)でVue Routerが使えるようになる Vueコンポーネントのルートもここに登録 router/index.js import Vue from 'vue' import VueRouter from 'vue-router' import BasicForm from '@/components/BasicForm.vue' import QuestionnaireForm from '@/components/QuestionnaireForm.vue' import ConsultationFrom from '@/components/ConsultationFrom.vue' import ConfirmationFrom from '@/components/ConfirmationFrom.vue' Vue.use(VueRouter) const routes = [ //ルートをいくつか定義 { //各ルートは 1 つのコンポーネントとマッピングされる必要がある(ここでは、path: '/') path: '/', //path: '宛先パス' name: 'BasicForm', //(name: コンポーネント名) component: BasicForm //conponent コンポーネント名 }, { path: '/questionnaire', name: 'QuestionnaireForm', component: QuestionnaireForm // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. // component: () => import(/* webpackChunkName: "about" */ '../views/QuestionnaireForm.vue')//これは何 }, { path: '/consultation', name: 'ConsultationFrom', component: ConsultationFrom }, { path: '/confirmation', name: 'ConfirmationFrom', component: ConfirmationFrom } ] const router = new VueRouter({ //ルーターインスタンスを作成して、ルートオプションを渡す mode: 'history', base: process.env.BASE_URL, routes // `routes: routes` の短縮表記 }) export default router 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】特定オブジェクト内のstateプロパティへのアクセス

Vue.js 学び dataプロパティ内の、lists配列内の、特定オブジェクト内のstateプロパティへのアクセス 参照 <template> <div id="app"> <div class="title"> <h1>Todoリスト</h1> </div> <div class="main"> <div class="switch"> <label><input type="radio" name="change-displey" onclick="changeDispley()" checked>すべて</label> <label><input type="radio" name="change-displey" onclick="changeDispley()" >作業中</label> <label><input type="radio" name="change-displey" onclick="changeDispley()" >完了</label> </div> <div class="task"> <table> <tr> <th>ID</th> <th>コメント</th> <th>状態</th> </tr> <tr v-for="(list, i) in lists" v-bind:key="i"> <td>{{ i }}</td> <td>{{ list.comment }}</td> <td v-if="list.state"><button @click="changeState(i)">作業中</button></td> <td v-else><button @click="changeState(i)">完了</button></td> <td><button @click="deleteList(i)">削除</button></td> </tr> </table> </div> </div> <div class="addTask"> <h1>新規タスクの追加</h1> <div class="form"> <form @submit.prevent> <input type="text" v-model="comment"> <input type="submit" value="送信" @click="addTodo"> </form> </div> </div> </div> </template> <script> export default { data() { return { comment: '', lists: [], } }, methods: { addTodo: function(){ const todo = { comment: this.comment, state: true, } this.lists.push(todo) }, deleteList(i) { this.lists.splice(i, 1) }, changeState(i) { this.lists[i].state = !this.lists[i].state } }, }; </script> ;は不要になりますね。エディターの自動整形を使用するといいですね。 <label><input type="radio" name="change-displey" onclick="changeDispley();" checked>すべて</label> <label><input type="radio" name="change-displey" onclick="changeDispley()" checked>すべて</label> functionを省略する記法もありますので、ぜひ使っていきましょう。 また{}の両外側にはスペースをあけるようにしましょう。 changeState: function(i){ ↓ changeState(i) { 【vue】methodsの省略表記と注意点(アロー関数は使えない)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでtopへ戻る機能を実装

Vue.jsで実装した機能を備忘録として記録する。 ※Vue-CLIを使用しています。 template <div class="footer"> <i class="icon-up-open-big scrollToTop" @click="scrollTop"></i> </div> js export default { data() { return { scrollY: 0 } }, mounted() { window.addEventListener('scroll', this.handleScroll); }, methods: { handleScroll() { this.scrollY = window.scrollY; let scrollToTop = document.getElementsByClassName("scrollToTop")[0]; if(scrollY > 100) { scrollToTop.style.display = "inline"; } else { scrollToTop.style.display = "none"; } }, scrollTop() { window.scrollTo({ top: 0, behavior: "smooth" }); } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

export defaultとは何か

備忘録です export defaultとは? 他のコンポーネントが外部から参照できるようにするもので、単一コンポーネント方式でよく使う。以下のようにscriptタグ内にコードを記述して使う <script> export default { //コード記述 } </script> 何故export defaultを使うのか VueCLIを使用する時点で、コンポーネント思想の考え方で作成することになる。コンポーネント思想は部品を組み合わせて一つのVueを作成するという考え方なので、作成したコンポーネントは他のコンポーネントも参照できなくてはいけない。 参照することで、参照したコンポーネントと自身で作成したコンポーネントを組み合わせることができる。 なので、VueCLIを使用するときは全てのの中にexport defaultは入れなくてはならない。 まとめ VueCLIを使用する以上、export default内でコードを書く必要がある
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】簡単なToDoアプリの制作と学習メモ

きっかけ これまでVueを用いてアプリを作ったりしていたのですが,Vueについての基本的なことが全然理解できていなかったことやFirebaseでサイトを公開してみたかったため学習を兼ねて作ってみました. 完成物 ToDoアプリ 構成 "Index.vue(src/pages/Index.vue)"という大枠のページの中に, Todoを書きこむ"TodoForm.vue(src/components/TodoForm.vue)" 書き込んだTodoを表示させる"TodoList.vue(src/components/TodoList.vue)" の2つのコンポーネントを組み込んだ形になります. スタイルはBootstrap-Vueを使いました. インポート Bootstrapの公式サイトに従ってコマンドを打ち込みます. npm install vue bootstrap bootstrap-vue 次にindex.js内でimport index.js import Vue from 'vue' import Router from 'vue-router' import Index from '@/pages/Index' import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' // Import Bootstrap an BootstrapVue CSS files (order is important) import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' // Make BootstrapVue available throughout your project Vue.use(BootstrapVue) // Optionally install the BootstrapVue icon components plugin Vue.use(IconsPlugin) Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Index', component: Index } ] }) Index.vue 大枠となるページのファイル Index.vue <template> <div> <h1>ToDoリスト</h1> <todo-form @handleParentAddTodo="handleParentAddTodo" /> <todo-list :todos="todos" @handleParentDeleteTodo="handleParentDeleteTodo" @handleParentCompleteTodo="handleParentCompleteTodo" /> </div> </template> <script> // TodoForm,TodoList読み込み import TodoForm from '@/components/TodoForm' import TodoList from '@/components/TodoList' export default { name: 'Todo', components: { TodoForm, TodoList }, data () { return { // データバインディング todos: [] } }, methods: { // v-onでメソッド呼び出し // Todoリスト追加処理 handleParentAddTodo (value) { if (value) { this.todos.unshift({ text: value, complete: false }) } }, // Todoリスト完了処理 // この処理でcompleteの真偽を逆転させる handleParentCompleteTodo (index) { this.todos[index].complete = !this.todos[index].complete }, // Todoリスト削除処理 // spliceで引数の一行を消去 handleParentDeleteTodo (index) { this.todos.splice(index, 1) } } } </script> <style> </style> TodoForm.vue Todoリストを書き込み送信するファイル handleAddTodo関数の$emitで子コンポーネント(TodoForm.vue)から親コンポーネント(Index.vue)へイベントを放っている TodoForm.vue <template> <b-container> <b-input-group> <!-- v-model属性で双方向ディバイディング --> <b-form-input type="text" v-model="value"/> <b-input-group-append> <b-button variant="primary" @click="handleAddTodo()">送信</b-button> </b-input-group-append> </b-input-group> </b-container> </template> <script> export default { name: 'TodoForm', data () { return { value: '' } }, methods: { // 送信ボタンクリック時に処理される関数 handleAddTodo () { // Index.vueのhandleParentAddTodoにフォーム内の値を引数に発火 this.$emit('handleParentAddTodo', this.value) this.value = '' } } } </script> <style> </style> TodoList.vue フォームで送信されたTodoリストを表示したり,進捗を操作するファイル TodoFormと同様にhandleCompleteTodo関数,handleDeleteTodo関数の$emitで子コンポーネント(TodoList.vue)から親コンポーネント(Index.vue)へイベントを放っている TodoList.vue <template> <b-container> <!-- for文でTodoリストをひとつづつ処理 --> <b-row :key="index" v-for="(todo, index) in todos" class="mt-2"> <!-- v-bindのclass属性によりcomplete時にstyleが適用 --> <b-col cols="8" :class="[{line: todo.complete}, 'text-left']"> <h5>{{todo.text}}</h5> </b-col> <b-col cols="4" class="text-right"> <b-button @click="handleCompleteTodo(index)" :variant="todo.complete ? '' : 'success'"> {{ todo.complete ? '完了' : '未完了'}} </b-button> <b-button @click="handleDeleteTodo(index)" variant="danger">削除</b-button> </b-col> </b-row> </b-container> </template> <script> export default { name: 'TodoList', // 親コンポーネントからv-bindで渡された値(todos)を受け取る props: ['todos'], methods: { // 完了ボタンクリック時に処理される関数 handleCompleteTodo (index) { // Index.vueのhandleParentCompleteTodoにフォーム内の値を引数に発火 this.$emit('handleParentCompleteTodo', index) }, // 削除ボタンクリック時に処理される関数 handleDeleteTodo (index) { // Index.vueのhandleParentDeleteTodoにフォーム内の値を引数に発火 this.$emit('handleParentDeleteTodo', index) } } } </script> <style scoped> .line { text-decoration: line-through; } </style> FirebaseでHosting 以下の記事を参考にさせていただきました まとめ Todoアプリの制作を通して,v-model,v-bind,v-onについて一通りさわることができた. propsと$emitという親コンポーネントと子コンポーネントをつなぐ概念について理解できた. Firebaseで簡単にサイト公開をすることができた.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Laravelでリポジトリパターンを実装する

事前確認 PHP7.3 Laravel7.30 DockerによるLaravel+Vueの環境構築済み 実装 /var/www/laravel $ mkdir -p app/{Repositories,Services}/User $ touch app/Repositories/Repository.php \ > app/Repositories/User/{UserRepositoryInterface,UserRepository}.php \ > app/Services/Service.php > app/Services/User/UserService.php app/Repositories/Repository.php <?php namespace App\Repositories; class Repository { protected $builder; public function getQuery() { return $this->builder->query(); } } app/Repositories/User/UserRepositoryInterface.php <?php namespace App\Repositories\User; interface UserRepositoryInterface { public function findById(string $id); } app/Repositories/User/UserRepository.php <?php namespace App\Repositories\User; use App\Repositories\Repository; use App\Models\User; class UserRepository extends Repository implements UserRepositoryInterface { public function __construct(User $user) { $this->builder = $user; } public function findById(string $id) { return $this->getQuery()->find($id); } } app/Services/Service.php <?php namespace App\Services; class Service { } app/Services/User/UserService.php <?php namespace App\Services\User; use App\Services\Service; use App\Repositories\User\UserRepositoryInterface; class UserService extends Service { protected $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } public function findById(string $id) { return $this->userRepository->findById($id); } } app/Models/User.php + protected $primaryKey = 'login_id'; + public $incrementing = false; ServiceProviderの登録 /var/www/laravel $ php artisan make:provider RepositoryServiceProvider app/Providers/RepositoryServiceProvider.php <?php namespace App\Providers; use App\Repositories\User\UserRepository; use App\Repositories\User\UserRepositoryInterface; // use App\Repositories\Admin\AdminRepository; // use App\Repositories\Admin\AdminRepositoryInterface; use Illuminate\Support\ServiceProvider; class RepositoryServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->bind( UserRepositoryInterface::class, UserRepository::class, ); // 以下の形式で随時追加 // $this->app->bind( // AdminRepositoryInterface::class, // AdminRepository::class, //); } /** * Bootstrap services. * * @return void */ public function boot() { // } } config/app.php 'providers' => [ // 中略  App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\RepositoryServiceProvider::class, // 追加 // 以下略 /var/www/laravel /var/www/laravel # php artisan config:cache Controllerから呼び出してみる /var/www/laravel $ php artisan make:controller UserController --resource routes/api.php Route::group(['middleware' => 'api'], function () { Route::get('/user/{id}', 'UserController@show')->name('user.show'); }); app/Http/Controllers/UserController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Services\User\UserService; class UserController extends Controller { protected $userService; public function __construct(UserService $userService) { $this->userService = $userService; } // 中略 /** * Display the specified resource. * * @param string $id * */ public function show(string $id) { $user = $this->userService->findById($id); return response($user, 200); } // 以下略 Adminモデルでも同じように実装する トレイト 管理者の認証機能がある場合は、あるテーブルに対する追加、更新、削除の操作が管理者か一般ユーザーのいずれによるものか判別したい場合の実装。 今回は呼び出さず実装だけ /var/www/laravel $ php artisan make:migration add_column_users database/migrations/2021_04_01_170631_add_column_users.php public function up() { Schema::table('users', function (Blueprint $table) { $table->string('created_by')->nullable()->after('remember_token'); $table->string('updated_by')->nullable()->after('created_by'); $table->string('deleted_by')->nullable()->after('updated_by'); $table ->foreign('created_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); $table ->foreign('updated_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); $table ->foreign('deleted_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropForeign('users_deleted_by_foreign'); $table->dropForeign('users_updated_by_foreign'); $table->dropForeign('users_created_by_foreign'); $table->dropColumn('deleted_by'); $table->dropColumn('updated_by'); $table->dropColumn('created_by'); }); } /var/www/laravel $ mkdir app/Models/Traits $ touch app/Models/Traits/{HandledByUser,HandledByAdmin}.php app/Models/Traits/HandledByUser.php <?php namespace App\Models\Traits; use Illuminate\Support\Carbon; trait HandledByUser { public function createByUser(array $attributes) { $this->fill($attributes); $this->save(); return $this; } public function updateByUser(array $attributes) { $this->fill($attributes); $this->save(); return $this; } /** * @return void */ public function deleteByUser() { $this->deleted_by = null; $this->deleted_at = Carbon::now(); $this->save(); } } app/Models/Traits/HandledByAdmin.php <?php namespace App\Models\Traits; use App\Models\Admin; use Illuminate\Support\Carbon; trait HandledByAdmin { /** * @param array $attributes * @param Admin $admin * @return mixed */ public function createByAdmin(array $attributes, Admin $admin) { $this->fill($attributes); $this->created_by = $admin->login_id; $this->updated_by = $admin->login_id; $this->save(); return $this; } /** * @param array $attributes * @param Admin $admin * @return mixed */ public function updateByAdmin(array $attributes, Admin $admin) { $this->fill($attributes); $this->created_by = $admin->login_id; $this->updated_by = $admin->login_id; $this->save(); return $this; } /** * @param Admin $admin * @return void */ public function deleteByAdmin(Admin $admin) { $this->deleted_by = $admin->login_id; $this->deleted_at = Carbon::now(); $this->save(); } } app/Models/User.php <?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; use App\Models\Traits\HandledByUser; use App\Models\Traits\HandledByAdmin; class User extends Authenticatable { use Notifiable; use SoftDeletes; use HandledByUser; use HandledByAdmin; protected $primaryKey = 'login_id'; public $incrementing = false; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'login_id', 'password', 'created_by', 'updated_by', 'deleted_by', ]; // 以下略
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Laravel + Vueで管理者認証機能を実装する

バージョン確認 PHP7.3 Laravel7.30 vue2.6.12 Dockerを使ってローカル環境構築済み 一般ユーザーの認証機能は実装済み 手順 一般ユーザーはusersテーブル、管理者画面はadminsテーブルを使う 管理者はlogin_idとpasswordでログイン、ログアウトできるところまでを実装 便宜上管理者の新規登録機能も実装し、実務ではマスター管理などに応用する app.tsに、常に管理者の認証状態をvuexで保持するよう記述する コンポーネント化とcssによるスタイリングは割愛 Laravel側の実装 /var/www/laravel $ php artisan make:model Models/Admin -m database/migrations/2021_03_31_120856_create_admins_table.php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admins', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('login_id'); $table->string('email'); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); $table->softDeletes(); $table->unique(['email', 'deleted_at'], 'users_email_deleted_at_unique'); $table->unique(['login_id', 'deleted_at'], 'users_login_id_deleted_at_unique'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admins'); } } /var/www/laravel $ php artisan migrate config/auth.php 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], // 追加 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], // 追加 'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], // 追加 'admins' => [ 'provider' => 'admins', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], app/Models/Admin.php <?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; class Admin extends Authenticatable { use Notifiable; use SoftDeletes; protected $table = 'admins'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'login_id', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; } app/Http/Middleware/Authenticate.php <?php namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Support\Facades\Route; // 追加 class Authenticate extends Middleware { protected function redirectTo($request) { if (!$request->expectsJson()) { // 変更 if (Route::is('admin.*')) { return route('admin.login'); } else { return route('login'); } } } } app/Http/Middleware/RedirectIfAuthenticated.php <?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) {     // 変更 if ($guard === 'user') { return redirect()->route('currentUser'); } elseif ($guard === 'admin') { return redirect()->route('currentAdmin'); } } return $next($request); } } /var/www/laravel $ mkdir -p app/Http/Controllers/Admin/Auth $ cp app/Http/Controllers/Auth/{RegisterController,LoginController}.php app/Http/Controllers/Admin/Auth/ routes/api.php Route::group(['middleware' => 'api'], function () { Route::get('/current_user', function () { return Auth::user(); })->name('currentUser'); Route::namespace('Auth')->group(function() { Route::post('/register', 'RegisterController@register')->name('register'); Route::post('/login', 'LoginController@login')->name('login'); Route::middleware('auth')->group(function() { Route::post('/logout', 'LoginController@logout')->name('logout'); }); }); // 追加 Route::get('/current_admin', function () { return Auth::guard('admin')->user(); })->name('currentAdmin'); Route::namespace('Admin')->prefix('admin')->name('admin.')->group(function () { Route::post('register', 'Auth\RegisterController@register')->name('register'); Route::post('login', 'Auth\LoginController@login')->name('login'); Route::post('logout', 'Auth\LoginController@logout')->name('logout'); }); Route::middleware('auth')->group(function () { // 一般認証が必要なAPI }); Route::middleware('auth:admin')->group(function () { // 管理者認証が必要なAPI }); }); app/Http/Controllers/Admin/Auth/RegisterController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\Admin; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Auth; class RegisterController extends Controller { /* |-------------------------------------------------------------------------- | Register Controller |-------------------------------------------------------------------------- | | This controller handles the registration of new users as well as their | validation and creation. By default this controller uses a trait to | provide this functionality without requiring any additional code. | */ use RegistersUsers; protected function guard() { return Auth::guard('admin'); } // 中略 /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:admin')->except('logout'); } /** * Get a validator for an incoming registration request. * 'users'を'admins'に変えるだけ * @param array $data * @return \Illuminate\Contracts\Validation\Validator */ protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'login_id' => ['required', 'regex:/^[\w\-\._]+$/i', 'min:8', 'max:20', Rule::unique('admins', 'login_id')->whereNull('deleted_at')], 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('admins', 'email')->whereNull('deleted_at')], 'password' => ['required', 'string', 'min:8', 'max:20', 'confirmed'], ]); } /** * @param Request $request * @param Admin $admin * @return Admin */ protected function registered(Request $request, $admin) { return $admin; } app/Http/Controllers/Admin/Auth/LoginController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use App\Models\Admin; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /* |-------------------------------------------------------------------------- | Login Controller |-------------------------------------------------------------------------- | | This controller handles authenticating users for the application and | redirecting them to your home screen. The controller uses a trait | to conveniently provide its functionality to your applications. | */ use AuthenticatesUsers; /** * Where to redirect users after login. * * @var string */ protected $redirectTo = RouteServiceProvider::HOME; protected function guard() { return Auth::guard('admin'); } /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:admin')->except('logout'); } /** * @return string */ public function username() { return 'login_id'; } /** * @param Request * @param Admin $admin * @return Admin */ protected function authenticated(Request $request, $admin) { return $admin; } /** * @param Request $request */ protected function loggedOut(Request $request) { Auth::logout(); $request->session()->regenerate(); return response()->json(); } } Vue側の実装 ~/workspace/myapp/resources/ts $ cp types/Models/User.ts types/Models/Admin.ts \ > store/auth.ts store/admin.ts resources/ts/types/Models/Admin.ts - export type User = { + export type Admin = { id: number name: string [k: string]: any } | null resources/ts/store/admin.ts - import { User } from '@/types/Models/User' + import { Admin } from '@/types/Models/Admin' import { RegisterRequest, LoginRequest } from '@/types/Auth' import { UNPROCESSABLE_ENTITY } from '@/util' import router from '@/router' export type State = { - user: User + admin: Admin } const state = { - user: null, + admin: null, } const getters = { - isLogin: (state: State): boolean => !!state.user, - userId: (state: State): number | null => (state.user ? state.user.id : null), - userName: (state: State): string => (state.user ? state.user.name : ''), + isLogin: (state: State): boolean => !!state.admin, + adminId: (state: State): number | null => (state.admin ? state.admin.id : null), + adminName: (state: State): string => (state.admin ? state.admin.name : ''), } const mutations = { - setUser(state: State, user: User): void { - state.user = user + setAdmin(state: State, admin: Admin): void { + state.admin = admin }, } const actions = { async register(context, data: RegisterRequest): Promise<void> { await window.axios - .post('/api/register', data) - .then((response) => { - context.commit('setAdmin', response.data) + .post('/api/admin/register', data) + .then((response) => { + context.commit('setUser', response.data) context.commit( 'error/setErrors', { messages: null, status: null, }, { root: true } ) - router.push('/mypage') + router.push('/admin/mypage') }) .catch((err) => { if (err.response.status === UNPROCESSABLE_ENTITY) { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) } }) }, async login(context, data: LoginRequest): Promise<void> { await window.axios - .post('/api/login', data) - .then((response) => { + .post('/api/admin/login', data) + .then((response) => { - context.commit('setUser', response.data) + context.commit('setAdmin', response.data) context.commit( 'error/setErrors', { messages: null, status: null, }, { root: true } ) - router.push('/mypage') + router.push('/admin/mypage') }) .catch((err) => { if (err.response.status === UNPROCESSABLE_ENTITY) { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) } else { context.commit('error/setErrors', { messages: err.response, status: err.response.status, }, { root: true } ) } }) }, async logout(context): Promise<void> { await window.axios - .post('/api/logout') + .post('/api/admin/logout') .then((response) => { - context.commit('setUser', null) - router.push('/login') + context.commit('setAdmin', null) + router.push('/admin/login') }) .catch((err) => { context.commit( 'error/setErrors', { messages: err.response, status: err.response.status, }, { root: true } ) }) }, - async currentUser(context): Promise<void> { + async currentAdmin(context): Promise<void> { await window.axios - .get('/api/current_user') - .then((response) => { - const user = response.data || null - context.commit('setUser', user) + .get('/api/admin/current_admin') + .then((response) => { + const admin = response.data || null + context.commit('setAdmin', admin) }) .catch((err) => { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) }) }, } export default { namespaced: true, state, getters, mutations, actions, } resources/ts/store/index.ts import Vue from 'vue' import Vuex from 'vuex' import auth from './auth' + import admin from './admin' import error from './error' Vue.use(Vuex) const store = new Vuex.Store({ modules: { auth, + admin, error }, }) export default store resources/ts/app.ts import Vue from 'vue' // ルーティングの定義をインポートする import router from './router' // ルートコンポーネントをインポートする import App from './App.vue' import store from './store' import bootstrap from './bootstrap' bootstrap() const createApp = async () => { await store.dispatch('auth/currentUser') + await store.dispatch('admin/currentAdmin') new Vue({ el: '#app', router, store, render: (h) => h(App), }) } createApp() view ~/workspace/myapp/resources/ts $ mkdir -p layouts/admin pages/admin/mypage $ touch layouts/admin/{Default,MyPageLayout}.vue \ > pages/admin/mypage/Index.vue $ cp pages/{Register,Login}.vue pages/admin/ resources/ts/layouts/admin/Default.vue <template> <div> <header> <nav> <ul v-if="!isLogin"> <li><router-link tag="a" to="/admin/login">管理者ログイン</router-link></li> <li><router-link tag="a" to="/admin/register">管理者登録</router-link></li> </ul> <ul v-else> <li @click="logout">ログアウト</li> </ul> </nav> </header> <main> <div class="container"> <router-view /> </div> </main> </div> </template> <script lang="ts"> import { Vue, Component } from 'vue-property-decorator' import { mapGetters } from 'vuex' @Component({ computed: { ...mapGetters({ isLogin: 'admin/isLogin' }) } }) export default class AdminLayout extends Vue { async logout(): Promise<void> { await this.$store.dispatch('admin/logout') } } </script> resources/ts/layouts/admin/MyPageLayout.vue <template> <div> <header> <nav> <ul v-if="!isLogin"> <li><router-link tag="a" to="/admin/login">管理者ログイン</router-link></li> <li><router-link tag="a" to="/admin/register">管理者登録</router-link></li> </ul> <ul v-else> <li @click="logout">ログアウト</li> </ul> </nav> </header> <main> <div class="container"> <router-view :admin="admin" /> </div> </main> </div> </template> <script lang="ts"> import { Vue, Component } from 'vue-property-decorator' import { mapState, mapGetters } from 'vuex' @Component({ computed: { ...mapState({ admin: state => state.admin.admin }), ...mapGetters({ isLogin: 'admin/isLogin', }), } }) export default class AdminMyPageLayout extends Vue { async logout(): Promise<void> { await this.$store.dispatch('admin/logout') } } </script> resources/ts/pages/admin/Login.vue <template> <div> <h1>管理者ログイン</h1> <form @submit.prevent="login"> <div v-for="(formItem, index) in submitData" :key="index"> <p v-if="formItem.errorMessages">{{ formItem.errorMessages[0] }}</p> <input v-if="formItem.type==='text'" type="text" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='password'" type="password" v-model="formItem.value" @keyup="validation(formItem)" :placeholder="formItem.placeholder" required > </div> <div> <button :disabled="disabled">ログイン</button> </div> </form> </div> </template> <script lang="ts"> import { Component, Mixins } from 'vue-property-decorator' import { Form } from '@/types/Form' import FormValidator from '@/mixins/formValidator' @Component export default class Login extends Mixins(FormValidator) { submitData: Form = { login_id: { name: 'login_id', type: 'text', value: '', rules: [(val: string) => !!val || 'ログインIDは必須です'], errorMessages: [], placeholder: 'login_id', }, password: { name: 'password', type: 'password', value: '', rules: [(val: string) => !!val || 'パスワードは必須です'], errorMessages: [], placeholder: 'at least 8 chars.', }, } async login(): Promise<void> { if (this.disabled) { Object.keys(this.submitData).forEach((key: string): void => { this.validation(this.submitData[key]) }) } else { const formData = new FormData() formData.append('login_id', this.submitData.login_id.value) formData.append('password', this.submitData.password.value) await this.$store.dispatch('admin/login', formData) } } } </script> resources/ts/pages/admin/Register.vue <template> <div class="container"> <h1>管理者登録</h1> <form @submit.prevent="register"> <div v-for="(formItem, index) in submitData" :key="index"> <p v-if="formItem.errorMessages">{{ formItem.errorMessages[0] }}</p> <input v-if="formItem.type==='text'" type="text" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='email'" type="email" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='password'" type="password" v-model="formItem.value" @keyup="validation(formItem)" :placeholder="formItem.placeholder" required > </div> <div> <button :disabled="disabled">管理者登録</button> </div> </form> </div> </template> <script lang="ts"> import { Component, Mixins} from 'vue-property-decorator' import { Form } from '@/types/Form' import FormValidator from '@/mixins/formValidator' @Component export default class Register extends Mixins(FormValidator) { submitData: Form = { name: { name: 'お名前', type: 'text', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => (val.length >= 2 && val.length <= 20) || '2文字以上20文字以下で設定してください', ], errorMessages: [], placeholder: 'your nickname', }, email: { name: 'メールアドレス', type: 'email', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^\w+[\w\-\._]*\w+@\w+[\w\-\._]*\.\w+[\w\-\._]*[A-Za-z]+$/i) || 'メールアドレスの形式が正しくありません' ], errorMessages: [], placeholder: 'sample@example.com', }, login_id: { name: 'ログインID', type: 'text', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^[\w\-\._]+$/g) || '半角英数字、「.」、「_」、「-」のみ使えます', (val: string) => val.length >= 8 && val.length <= 20 || '8文字以上20文字以下で入力してください' ], errorMessages: [], placeholder: 'login ID', }, password: { name: 'パスワード', type: 'password', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]+$/) || '半角小文字、半角大文字、数字をそれぞれ1種類以上含めてください', (val: string) => (val.length >= 8 && val.length <= 20) || '8文字以上20文字以下で設定してください', ], errorMessages: [], placeholder: 'at leeast 8 chars.', }, password_confirmation: { name: 'パスワード(確認用)', type: 'password', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => this.submitData.password.value === val || 'パスワードが一致しません' ], errorMessages: [], placeholder: 'confirm', }, } async register(): Promise<void> { if (this.disabled) { Object.keys(this.submitData).forEach((key: string): void => { this.validation(this.submitData[key]) }) } else { const formData = new FormData() formData.append('name', this.submitData.name.value) formData.append('email', this.submitData.email.value) formData.append('login_id', this.submitData.login_id.value) formData.append('password', this.submitData.password.value) formData.append('password_confirmation', this.submitData.password_confirmation.value) await this.$store.dispatch('admin/register', formData) } } } </script> resources/ts/pages/admin/mypage/Index.vue <template> <div> <p v-if="!!admin">{{ admin.name }}さん、管理者画面へようこそ</p> </div> </template> <script lang="ts"> import { Vue, Component, Prop } from 'vue-property-decorator' import { Admin } from '@/types/Models/Admin' @Component export default class AdminMyPageIndex extends Vue { @Prop({ type: Object, required: true, default: null }) admin!: Admin } </script> resources/ts/router.ts import Vue from 'vue' import VueRouter from 'vue-router' // ページコンポーネントをインポートする import Index from './pages/Index.vue' // レイアウト import DefaultLayout from './layouts/Default.vue' import MyPageLayout from './layouts/MyPageLayout.vue' + import AdminDefaultLayout from './layouts/admin/Default.vue' + import AdminMyPageLayout from './layouts/admin/MyPageLayout.vue' // ユーザー画面 import Login from './pages/Login.vue' import Register from './pages/Register.vue' import MyPageIndex from '@/pages/mypage/Index.vue' // 管理者画面 + import AdminLogin from '@/pages/admin/Login.vue' + import AdminRegister from '@/pages/admin/Register.vue' + import AdminMyPageIndex from '@/pages/admin/mypage/Index.vue' import store from './store' // VueRouterプラグインを使用する Vue.use(VueRouter) // パスとコンポーネントのマッピング const routes = [ { path: '/', component: DefaultLayout, children: [ { path: '/', component: Index, }, { path: '/register', component: Register, beforeEnter (to, from, next) { if (store.getters['auth/isLogin']) { next('/') } else { next() } } }, { path: '/login', component: Login, beforeEnter (to, from, next) { if (store.getters['auth/isLogin']) { next('/') } else { next() } } }, ], }, { path: '/mypage', component: MyPageLayout, beforeEnter (to, from, next) { if (!store.getters['auth/isLogin']) { next('/login') } else { next() } }, children: [ { path: '/', component: MyPageIndex, } ], }, // ↓追加 { path: '/admin', component: AdminDefaultLayout, children: [ { path: '/', component: AdminRegister, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next('/admin/login') } } }, { path: 'register', component: AdminRegister, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next() } } }, { path: 'login', component: AdminLogin, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next() } } }, ], }, { path: '/admin/mypage', component: AdminMyPageLayout, beforeEnter (to, from, next) { if (!store.getters['admin/isLogin']) { next('/admin/login') } else { next() } }, children: [ { path: '/', component: AdminMyPageIndex, } ], }, ] const router = new VueRouter({ mode: 'history', //URL中の#(ハッシュ)を消す routes }) export default router
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む