laravel＋vueでrole,permissionごとにいろいろやりたい→laravel-permissionを使ってみる！

#　はじめに
　laravel+vueでどうroleやpermissionを使ったものかよくわからなかったため、
　調べた結果を少しでも残せればと思い書きました。

　間違っているところもたくさんあると思うので、よければ指摘してやってください。

#　環境
　php　7.2.12
　laravel　6.0.3

#　何を作るか

  　admin,userの二種類用意し、
　adminには**隠しページに行ける許可**、**すべてのuserを閲覧できる許可**、**userの削除の許可**、
　userには**すべてのuserを閲覧できる許可**のみを与えます。
# 1. laravel-permissionを使おう！
##1-1　最初の設定

　```laravel new お好みで``` 
　から.envのDB設定までは省略します。

##  1-2 make:auth(laravel 6.0では消滅しているので別の方法を使う)

　**6.0以前**

   ```php artisan make:auth```

　**6.0から**　

   ```
   composer require laravel/ui

   php artisan ui vue --auth
  ```
　このあと6.0なら**npm　install**と**npm run dev**をやってください。

## 1-3 laravel-permissionのインストール
 
```composer require spatie/laravel-permission```
 
 config/app.phpに追加

 ```php:config/app.php
   'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];
 ```

そのあとmigrationをします。

```
 php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"

 php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"

 php artisan migrate
```
忘れずにUserModelにHasRolesトレイトを追加しておきましょう。

```php:user.php
class User extends Authenticatable
{
    use Notifiable;
    use HasRoles;//これを追加！
}
```

## 1-4 seedingによりRoleとPermissionを設定する
  
  
　**role**は**admin**、**user**の二つ、
　**permission**は**read_hiddenpage**、**read_allusers**、**delete_user**の三つとします。
　

|role|permission|
|:---:|:---:|
|user|read_allusers|
|admin|read_allusers,delete_user,read_hiddenpage|
という風にしていきます。


 まずroleTableからseeding

 ``` php artisan make:seeder RoleTableSeeder
 ```

 ```php:RoleTableSeeder.php
 <?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;//ここを忘れないように！

class RoleTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $roles = [
            "admin",
            "user"
        ];
        foreach ($roles as $role) {
            Role::create(["name" => $role]);
        }
    }
}
```

permissionTable

```php artisan make:seeder RoleTableSeeder```

```php:PermissionTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;

class PermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $permissions = [
            "read_hiddenpage",
            "read_allusers",
            "delete_user"
        ];
        foreach ($permissions as $permission) {
            Permission::create(["name" => $permission]);
        }
    }
}
```

RoleHasPermissionTable,ここで各roleにpermissionを与えていきます。

```php artisan make:seeder RoleHasPermissionTableSeeder```

```php:RoleHasPermissionTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;

class RoleHasPermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //admin
        $permissions = [
            "read_hiddenpage",
            "read_allusers",
            "delete_user"
        ];
        //一時変数使いたくないならtapで
        $role = Role::findByName("admin");
        $role->givePermissionTo($permissions);

        //user
        $permissions = [
            "read_alluser"
        ];
        $role = Role::findByName("user");
        $role->givePermissionTo($permissions);
    }
}
```


最後にUserをadminとuserの二種作ります。

　```php artisan make:seeder UserTableSeeder```

 ```php:UserTableSeeder.php
<?php

use App\User;
use Illuminate\Database\Seeder;


class UserTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //admin
        $user = User::create([
            "name" => "管理者",
            "email" => "admin@test",
            "password" => Hash::make('admin'),
        ]);
        $user->assignRole("admin");

        //user
        $user = User::create([
            "name" => "ユーザー",
            "email" => "user@test",
            "password" => Hash::make('user'),
        ]);
        $user->assignRole("user");
    }
}
```

##  1-5 Seedingの実行

　```
 composer dump-autoload
 php artisan db:seed --class=PermissionTableSeeder
 php artisan db:seed --class=RoleTableSeeder
 php artisan db:seed --class=RoleHasPermissionTableSeeder
 php artisan db:seed --class=UserTableSeeder
```

 
#  2.Vue側の設定

## 2-1　VueComponentにとりかかろう！
home.blade.phpをVueが使えるように変更しておく

```php:home.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="{{asset('css/app.css')}}">
    <title>test</title>
</head>
<body>
    <div id="app">
    <example-component></example-component>
    </div>
    <script src="{{asset('js/app.js')}}"></script>
</body>
</html>
```

 このままじゃlogoutができないので、mountしたExampleComponentにlogoutMethodを作ってあげる。

 axios.post(/logout)すれば良くて、これでLoginController@logoutが起動される(本体は 
 Illuminate\Foundation\Auth\AuthenticatesUsersにある)
 
 このままじゃredirectされないのでaxios.post(/logout).then(location="/")でredirectしてあげる。

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

          <div class="card-body">
            I'm an example component.
            <button @click="laraLogout">logout</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  mounted() {
    console.log("Component mounted.");
  },
  methods: {
    laraLogout() {
      axios
        .post("/logout")
        .then(response => {
          location = "/";
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>
```
##2-2 ほんのちょっとだけlaravel延長戦
vueでpermissionを使うために少しだけlaravel側をいじっておきます。

user.phpにアクセサーを追加します。

```php:user.php
  public function getAllPermissionsAttribute()
    {
        $permissions = [];
        foreach (Permission::all() as $permission) {
            if (auth()->user()->can($permission->name)) {
                //もし今ログインしているユーザーがこのpermissionを持っていれば
                $permissions[] = $permission->name;
            }
        }

        //今ログインしているユーザーの持っているpermissionを返してあげる
        return $permissions;
    }
```

↑で作った$permissionsを、home.blade.phpで受け取ってVue上でGlobalに使えるようにします。

```.php:home.blade.php
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="{{asset('css/app.css')}}">
    <title>test</title>
    {{-- ここに追加！！ --}}
    <script>
     @auth
      window.Permissions = @json(auth()->user()->allPermissions);
     @else
      window.Permissions = [];
     @endauth
    </script>
     {{-- ここに追加！！ --}}
</head>
```

今ログインしているユーザーがPermissionを持っているか判定する関数をGlobalに作ります。
(Mixinを使うんですが、あまりMixinはよくないとの意見もあるみたいなのでpluginにするかもしれません。)

resources/js/mixins/Permissions.jsを作ります。
![laravel-permissionQiitaScreen1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/b39b186b-0603-aede-c62d-284d29cd1d6b.png)

Permissions.jsの中身は写真と同じようにしてください。

resources/js/app.jsでPermissionsをmixin

```js:app.js
 import permissions from "./mixins/Permissions.js";
 Vue.mixin(permissions);
```

あとは使いたい場所で$can(判定したいpermission)を使います。試しに書いてみましょう。

```vue:ExampleComponent.vue
 <div class="card-body">
            I'm an example component.
            <button @click="laraLogout">logout</button>
          </div>
         <!-- ここに追加！ -->
          <div v-if="$can('read_allusers')">readallUsers</div>
          <div v-if="$can('read_hiddenpage')">readhiddenPage</div>
```

成功していればこのようになっていると思います。
![laravelpermissionGif1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/ac455c80-9de1-e313-1ffa-97da962215e4.gif)


## 2-3 vue-routerを使おう！




　``` npm install vue-router```をします。
　router.jsを作る前に少し以前のファイルに変更するのと、２つComponentを作りたいのでそこから。
　
まずはhome.blade.phpから。

```php:home.blade.php
 <body>
    <div id="app">
    {{-- ここを変更！ --}}
    <router-view></router-view>
    </div>
    <script src="{{asset('js/app.js')}}">
    </script>

</body>
```

ExampleComponent

```vue:ExampleComponent.vue
 <div class="card-body">
            I'm an example component.
            <button @click="laraLogout">logout</button>
          </div>
          <!-- ここに追加！ -->
          <div>
            <router-link to="/allusers">read_allUsers</router-link>
          </div>
          <div>
            <router-link to="/hiddenpage">read_hiddenPage</router-link>
          </div>
```

2つComponentを作ります。
AllUsers

```vue:AllUsers.vue
 <template>
  <div>
    <div>allUsers</div>
    <div v-if="$can('delete_user')">can delete User</div>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>
```

HiddenPage

```vue:HiddenPage.vue
 <template>
  <div>This is Hidden Page</div>
</template>

<script>
export default {};
</script>

<style>
</style>
```
  以上です。お疲れさまでした。


 変更も終わったので、js/router/router.jsを作ります。

![laravel-permissionQiitaScreen2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/c7542dff-892a-473b-979b-d37c45c0249e.png)

```js:router.js
import Vue from "vue";
import VueRouter from "vue-router";
import HiddenPage from "../components/HiddenPage.vue";
import AllUsers from "../components/AllUsers.vue";
import Example from "../components/ExampleComponent.vue";

Vue.use(VueRouter);
const $can = permissionName => {
    return Permissions.indexOf(permissionName) !== -1;
};

const routes = [
    {
        path: "/hiddenpage",
        component: HiddenPage,
        beforeEnter(to, from, next) {
            if ($can("read_hiddenpage")) {
                next();
            } else {
                //errorPageに飛ばしたい場合はエラーページを作って、ここのnextに入れてあげればいい
                alert("403 forbidden");
            }
        }
    },
    {
        path: "/allusers",
        component: AllUsers
    },
    {
        path: "/home",
        component: Example
    }
];

const router = new VueRouter({
    routes,
    hashbang: false,
    mode: "history"
});

export default router;
```

あとはapp.jsにrouter.jsを登録するだけです。

```js:app.js
 //////

 import router from "./router/router";

 //////
 const app = new Vue({
    el: "#app",
    router
});
```
ここまでで以下のような動きになっていると思います。長々とありがとうございました。
![laravelpermissionGif2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/7f7bf6a5-bed7-7d68-ee98-e65296d1cd05.gif)





#　おわりに
どこまで端折っていいかの区別ができていなくて無駄に長くなってしまった感じがします。
課題としてlaravelのmiddlewareによるguardとvue-routerのguardをどう組み合わせていけばいいかがわからないままでした。
組み合わせなくてもどちらか一方でいいんですかね・・・？
どなたかアドバイスをいただければものすごく助かります。
















　
　
　


#　参考
##laravel-permissionについて
 -  [laravel-permissionの使い方](https://qiita.com/beeete2/items/0cb2ee0e6341c9be7075)
 -  [laravel-permissionを試す](https://qiita.com/sh-ogawa/items/09b7097b5721dcdbe566)
 -  [Laravelでユーザーごとの権限を管理するlaravel-permissionをインストールする](https://econosys-system.com/blog/archives/342)
 -  [Create an Admin middleware for Laravel with spatie/laravel-permission](https://medium.com/swlh/create-an-admin-middleware-for-with-spatie-laravel-permission-6419152049cf)
 
　
　

##  Vueについて
 -  [Laravel Permissions in Vue Components](https://mmccaff.github.io/2018/11/03/laravel-permissions-in-vue-components/)
 -  [Laravel5.7 + Vue2.5 でLaravel ユーザでログインして Vue画面を表示する](https://qiita.com/nobu-maple/items/f0ee9324eacfed6eddd7)