STEP03：Laravel5.7 + Vue2.5 でLaravel ユーザの権限によってvueコンポーネントを出しわける

※ 2018/10/15 Laravel5.6 から 5.7 に記述を更新

Laravelに登録しているユーザでログイン処理をして、ユーザの権限によって表示する vueコンポーネントを出し分けてみます
Laravelの権限は [GATE 機能](https://readouble.com/laravel/5.7/ja/authorization.html) を利用してみます


環境設定他関連記事はこちら
[Laravel + Vue + Vuetify で業務サイト作ってみる](https://qiita.com/nobu-maple/items/d1e7170d62ab07890a7a)


とりあえず、権限は「管理者 Admin」と「ユーザ User」の２種類のみ
隠し設定？で「システム管理者 System」も一応定義だけはしておきます

権限 role 系の設定等についてはこちらを参考にさせていただきました
[LaravelのGate(ゲート)機能で権限(ロール)によるアクセス制限を実装する](https://www.ritolab.com/entry/56)

#１．ユーザテーブルに権限roleカラムを追加

## ユーザテーブル変更
ほんとはカラム追加のmigrateをするべきなんでしょうけど、Userテーブル作成ファイルを直接編集しちゃいます

```php:database/migrations/2014_10_12_000000_create_users_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('loginid')->unique()->comment('ログインID');
            $table->string('password');
            $table->tinyInteger('role')->unsigned()->default(10)->comment('権限　0:system  5:admin  10:user');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
```

##初期データ追加部分変更
管理者ユーザと一般ユーザを作成して、ダミーデータを１００件登録

```php:database/seeds/UsersTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class UsersTableSeeder extends Seeder
{
    /**
    * Run the database seeds.
    *
    * @return void
    */
    public function run()
    {
        DB::table('users')->insert(
        [
            [
                'name' => 'メイプル　管理者',
                'loginid' => 'maple_admin',
                'password' => Hash::make('password'),
                'role' => 5,
            ],
            [
                'name' => 'メイプル　ユーザ',
                'loginid' => 'maple_user',
                'password' => Hash::make('password'),
                'role' => 10,
            ],
        ]);

        factory(App\User::class, 100)->create();
    }
}
```

##ダミーデータ作成部分変更
ダミーデータも role を追加しときます
role の出現率？はユーザ権限を多めにしておきます

```php:database/factories/UserFactory.php
<?php

use Faker\Generator as Faker;
use Illuminate\Support\Facades\Hash;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'loginid' => $faker->unique()->safeEmail,
        'password' => Hash::make('password'),
        'role' => $faker->randomElement($array = array (5, 10, 10, 10, 10, 10)),
    ];
});
```

##モデルも変更

```php:app/User.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
//        'email',
        'loginid',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'role',
        'remember_token',
    ];
}
```

##変更を反映
```bash
php artisan migrate:refresh --seed
```


#２．Laravel GATE の設定
参考サイトをほぼそのまま真似しておきます
※ boot 内に権限毎の定義（Gate::define）をしておきます
[LaravelのGate(ゲート)機能で権限(ロール)によるアクセス制限を実装する](https://www.ritolab.com/entry/56)

```php:app/Providers/AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        // システム管理者(role 0)のみ許可
        Gate::define('system', function ($user) {
            return ($user->role == 0);
        });

        // 管理者以上（システム管理者(role 0) ＆ 管理者(role 5)）に許可
        Gate::define('admin', function ($user) {
            return ($user->role <= 5);
        });

        // 一般ユーザ(role 10)以上（つまり全権限）に許可
        Gate::define('all', function ($user) {
            return ($user->role <= 10);
        });

        // 一般ユーザのみ許可
        Gate::define('user', function ($user) {
            return ($user->role == 10);
        });
    }
}
```

GATE 指定によって以下の動作になる
　'system' とすると、システム管理者(role 0)のみ許可
　'admin' とすると、システム管理者(role 0) と 管理者(role 5)に許可
　'user'　とすると、ユーザ(role 10)のみ許可
　'all'　とすると、システム管理者、管理者、一般ユーザに許可





#３．管理者用コンポーネント作成
作成済みの ExampleComponent.vue をコピーして 管理者用コンポーネントを作成します。
どちらの権限で入っているのかを分かりやすくするために、ユーザ名や role も表示できるように改造もしときます。


##ユーザ用コンポーネント

GETで表示をやめて、ユーザ名やらを表示するように変更

```html:resources/js/components/ExampleComponent.vue
<template>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-md-8">
        <div class="card card-default">
          <div class="card-header">Example Component</div>

          <div class="card-body">
            I'm an example component.
            <div class="roledetail">
              ID: {{ id }}<br>
              Name: {{ name }}<br>
              Role: {{ role }}<br>
            </div>
            <button v-on:click="axiosLogout">logout</button>
          </div>

        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.roledetail {
  color: red;
  font-size: 4vw;
}
</style>

<script>
export default {
  props: {
    id: String,
    name: String,
    role: String,
    logout: String,
  },

  mounted() {
    console.log('ExampleComponent mounted.')
    console.log('name: '+ this.name)
  },

  methods: {
    axiosLogout() {
      axios.post(this.logout)
      .then( function (response) {
        console.log(response)
      }.bind(this))

      .catch(function (error) {
        console.log(error)
        if (error.response) {
          if (error.response.status) {
            if (error.response.status == 401 || error.response.status == 419) {
              var parser = new URL(this.logout)
              location.href=parser.origin
            }
          }
        }
      }.bind(this))
    },
  },
}
</script>
```


##管理者用コンポーネント
ユーザ用からコピーして

```bash
$ cp resources/js/components/ExampleComponent.vue resources/js/components/AdminComponent.vue
```

中身の名称部分を変更


```html:resources/js/components/AdminComponent.vue
<template>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-md-8">
        <div class="card card-default">
          <div class="card-header">Admin Component</div>

          <div class="card-body">
            I'm an admin component.
            <div class="roledetail">
              ID: {{ id }}<br>
              Name: {{ name }}<br>
              Role: {{ role }}<br>
            </div>
            <button v-on:click="axiosLogout">logout</button>
          </div>

        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.roledetail {
  color: blue;
  font-size: 4vw;
}
</style>

<script>
export default {
  props: {
    id: String,
    name: String,
    role: String,
    logout: String,
  },

  mounted() {
    console.log('AdminComponent mounted.')
    console.log('name: '+ this.name)
  },

  methods: {
    axiosLogout() {
      axios.post(this.logout)
      .then( function (response) {
        console.log(response)
      }.bind(this))

      .catch(function (error) {
        console.log(error)
        if (error.response) {
          if (error.response.status) {
            if (error.response.status == 401 || error.response.status == 419) {
              var parser = new URL(this.logout)
              location.href=parser.origin
            }
          }
        }
      }.bind(this))
    },
  },
}
</script>
```


##管理者用コンポーネント取り込み

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

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue'));
Vue.component('admin-component', require('./components/AdminComponent.vue'));

const app = new Vue({
    el: '#app'
});
```



## Laravel側からの呼び出し部分
GATEで管理者用コンポーネントとユーザ用コンポーネントを出し分け
vue 側にLaravelのユーザ名とか権限(role)とかも渡しておく


```html:resources/views/home.blade.php
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>Laravel5.7 + Vue.js2.5</title>

    <!-- Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
    <style>
    html, body {
        background-color: #fff;
        color: #636b6f;
        font-family: 'Nunito', sans-serif;
        font-weight: 200;
        height: 100vh;
        margin: 0;
    }
    </style>
</head>
<body>
    <div id="app">
        @can('admin')
            <admin-component
                id="{{ Auth::user()->id }}"
                name="{{ Auth::user()->name }}"
                role="{{ Auth::user()->role }}"
                logout="{{ route('logout') }}"
            ></admin-component>
        @else
            <example-component
                id="{{ Auth::user()->id }}"
                name="{{ Auth::user()->name }}"
                role="{{ Auth::user()->role }}"
                logout="{{ route('logout') }}"
            ></example-component>
        @endcan
    </div>
    <script src=" {{ mix('js/app.js') }} "></script>
</body>
</html>
```



#４．権限毎にコンポーネントが変わっていることを確認
コンポーネントをいじっているのでコンパイルしてから、laravelを起動

```bash
npm run dev
php artisan serve --host=172.16.0.100  --port=8000
```

ブラウザでアクセスして
http://172.16.0.100:8000/

↓↓

ログインページへ飛ばされて
http://172.16.0.100:8000/login

ログインでパスワードとかを間違えるとエラーが表示されて
![172.16.0.100_8000_login.png](https://qiita-image-store.s3.amazonaws.com/0/153259/bcfe1d0c-def2-34fa-2391-20fb71445986.png)

↓↓

Admin の管理者権限でログインしたら

Adminのコンポーネントが表示されて、
![172.16.0.100_8000_.png](https://qiita-image-store.s3.amazonaws.com/0/153259/095db0d7-581a-725e-135a-4aa94705d399.png)

ログアウトボタンでログアウトしたら

↓↓

ログインページへ戻って、
User のユーザ権限でログインしたら
![172.16.0.100_8000_.png](https://qiita-image-store.s3.amazonaws.com/0/153259/31deef26-d9b5-1081-08d6-131146bb97e5.png)

ユーザ用のコンポーネントが表示されること

以上

環境設定他関連記事はこちら
[Laravel + Vue + Vuetify で業務サイト作ってみる](https://qiita.com/nobu-maple/items/d1e7170d62ab07890a7a)

ソースはこちら
https://github.com/u9m31/u9m31/tree/step03