Laravel5.5こと始め 〜8. Vue.jsとAPIベースのユーザ管理アプリの追加〜

# 内容
以下の順番にまとめます。
[1. MacへのXAMPP+Laravelインストール](https://qiita.com/sonrisa/items/f08112fe0ec4f51e4bdf)
[2. ユーザログイン機能の追加](https://qiita.com/sonrisa/items/207fd53eced13fc88acf)
[3. MVCとルーティングの説明]
(https://qiita.com/sonrisa/items/809adf872e6fb3657f81)
[4. ユーザリストの表示]
(https://qiita.com/sonrisa/items/929486929ed8d851a24e)
[5. ユーザリストのペジネーション表示]
(https://qiita.com/sonrisa/items/86efb8caebe207ab8fa8)
[6. ユーザ管理APIの追加](https://qiita.com/sonrisa/items/bc4894e6803632089ceb)
[7. Vue.jsとAPIベースのユーザ管理アプリの追加準備](https://qiita.com/sonrisa/items/595f080c6ecf1ed61b93)
[8. Vue.jsとAPIベースのユーザ管理アプリの追加](https://qiita.com/sonrisa/items/48a9a8e73e757c81619a) ←いまここ
[9. Vue.jsとAPIベースのユーザ管理アプリへのペジネーション追加]
(https://qiita.com/sonrisa/items/d58c56b638924d8c6810)
[10. APIへのJWTAuth認証の追加]
(https://qiita.com/sonrisa/items/97c1f3e8491f76dc2ce2)
[11. Vue.jsとAPIベースのユーザ管理アプリへの認証の追加](https://qiita.com/sonrisa/items/1976cbd049cadfec1285)

# 8. Vue.jsとAPIベースのユーザ管理アプリの追加
前回は、ログイン後 http://localhost:8000/home2 にアクセスすると「resources/views/home2.blade.php」が表示されるところまでを実装しましたが、ここではVue.jsのコンポーネントを作成します。

<img width="1231" alt="スクリーンショット 2018-07-18 16.05.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/8ba28535-3e26-34af-3bee-7a95eb94c04b.png">

Vue.jsのコンポーネントとは再利用可能なWebのUI部品のようなもので、上図で表したファイルに以下のルールでコードを作成して「npm run dev」というコマンドでコンパイルをするとHTMLの中で&lt;router-view/>タグなどを差し込むことで、コンポーネントを使うことができるようになります。

### Vueコンポーネントの作成ステップ

#### 1. home2.blade.phpの作成(太緑枠)
下記の通りViewである「resources/views/home2.blade.php」を作成します。(こちらは「[7. Vue.jsとAPIベースのユーザ管理アプリの追加準備](https://qiita.com/sonrisa/items/595f080c6ecf1ed61b93)」で作成しました)

```resources/views/home2.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-body">
                    <router-view/>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

「<span>@</span>extends('layouts.app')」は、「resources/views/layouts/app.blade.php」をページレイアウトとして定義して、「app.blade.php」に記載された「<span>@</span>yield('content')」部分に上記「home2.blade.php」の「<span>@</span>section('content')」から「<span>@</span>endsection」までのHTMLを入れ込んで表示することを表しています。

また、「app.blade.php」には「&lt;script src="{{ asset('js/app.js') }}" defer></script&gt;」と記載があるため、上図にあるようにVueで最終的に生成される（"npm run dev"コマンドで生成される）「public/js/app.js」が読み込まれることになります。

さらに、「&lt;router-view/&gt;」タグはview-routerライブラリを使って作成するVueコンポーネントをセットすることを表します。

#### 2. app.blade.phpの作成(細黒枠)

「resources/views/layouts/app.blade.php」はレイアウトとして使うため、すべてのページで呼び出されます。そのため、APIの認証のためのアクセストークンもこちらでセットします。

```resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', 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">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <!-- ここから追加 -->
    <script>
      window.Laravel = {};
      window.Laravel.csrfToken = "{{ csrf_token() }}";
    </script>
    <!-- ここまで追加 -->

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>
... (以下省略)
```

#### 3. app.jsの作成(赤枠)
Vueライブラリの読み込みと.vueファイルで定義するVueコンポーネントの名前定義やview-routerライブラリを使ったルーティング定義は「resources/assets/js/app.js」で行います。

```resources/assets/js/app.js
require('./bootstrap');
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import http from './http.js';

const routes = [
  {path:'/', component:require('./components/UsersIndex.vue'), name:'listUsers'},
  {path:'/create', component:require('./components/UsersCreate.vue'), name:'createUser'},
  {path:'/edit/:id', component:require('./components/UsersEdit.vue'), name:'editUser'},
]

const router = new VueRouter({ routes })
new Vue(Vue.util.extend({
  router,
  created () {
    http.init()
  },
})).$mount('#app');
```

「import VueRouter from 'vue-router';」で、VueRouterという名前でvue-routerライブラリのインスタンスを作成しています。

```app.js
const routes = [
  {path:'/', component:require('./components/UsersIndex.vue'), name:'listUsers'},
  {path:'/create', component:require('./components/UsersCreate.vue'), name:'createUser'},
  {path:'/edit/:id', component:require('./components/UsersEdit.vue'), name:'editUser'},
]
```

以上の定義により、URIが「http://localhost:8000/home2#/ 」だと「resources/assets/js/components/UsersIndex.vue」で定義された「listUsers」という名前の「UsersIndex」コンポーネントを「home2.blade.php」の「&lt;router-view/&gt;」タグに当てはめ、URIが「http://localhost:8000/home2#/create 」だと「resources/assets/js/components/UsersCreate.vue」で定義された「createUser」という名前の「UsersCreate」コンポーネントを「home2.blade.php」の「&lt;router-view/&gt;」タグに当てはめ、URIが「http://localhost:8000/home2#/edit/:id 」(:idにはユーザIDを当てはめます)だと「resources/assets/js/components/UsersEdit.vue」で定義された「editUser」という名前の「UsersEdit」コンポーネントを「home2.blade.php」の「&lt;router-view/&gt;」タグに当てはめることを表します。

#### 4. http.jsの作成（細赤枠）

上記「app.js」内で「import http from './http.js';」と宣言されているAPI実行用のJavaScriptファイルを作成します。JSファイルとして分離することによって、初期設定などコードの見通しがよくなります。

APIへのアクセスはaxiosライブラリを利用しており、get,post,put,deleteメソッドのほか初期化関数initを実装しており、initではトークンなどヘッダー情報をセットしています。

```resources/assets/js/http.js
import axios from 'axios'
export default {
  request(method,url,data,successCb=null,errorCb=null) {
    axios.request({
      url,
      data,
      method: method.toLowerCase()
    }).then(successCb).catch(errorCb)
  },
  get(url,successCb=null,errorCb=null){
    return this.request('get',url,{},successCb,errorCb)
  },
  post(url,data,successCb=null,errorCb=null) {
    return this.request('post',url,data,successCb,errorCb)
  },
  put(url,data,successCb=null,errorCb=null) {
    return this.request('put',url,data,successCb,errorCb)
  },
  delete(url,data={},successCb=null,errorCb=null) {
    return this.request('delete', url, data, successCb, errorCb)
  },
  init() {
    axios.defaults.baseURL = 'http://localhost:8000/api'
    axios.interceptors.request.use(config => {
      config.headers['X-CSRF-TOKEN'] = window.Laravel.csrfToken
      config.headers['X-Requested-With'] = 'XMLHttpRequest'
      console.log(config)
      return config
    })
  }
}
```

#### 5. UsersIndex.vueの作成(青枠)

```resources/assets/js/components/UsersIndex.vue
<template>
  <div>
    <div class="form-group">
      <router-link :to="{name: 'createUser'}" class="btn btn-success">Create new user</router-link>
    </div>
    <div class="panel panel-default">
      <div class="panel-body">
        <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th>Name</th>
            <th>Email</th>
            <th width="100">&nbsp;</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="user, index in users">
            <td>{{ user.name }}</td>
            <td>{{ user.email }}</td>
            <td>
              <router-link :to="{name:'editUser',params:{id:user.id}}" class="btn btn-xs btn-default">Edit</router-link>
              <a href="#" class="btn btn-xs btn-danger" v-on:click="deleteEntry(user.id, index)">Delete</a>
            </td>
          </tr>
        </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

<script>
  import http from '../http.js'
  export default {
    data: function () {
      return {
        users: []
      }
    },
    mounted() {
      this.getResults();
    },
    methods: {
      getResults() {
        var app = this;
        http.get('users',resp=>{
          app.users = resp.data;
        })
      },
      deleteEntry(id, index) {
        if (confirm("Do you really want to delete it?")) {
          var app = this;
          http.delete('users/'+id,app.$router.go(0))
        }
      }
    }
  }
</script>
```
#### 6. UsersCreate.vueの作成(青枠)

```resources/assets/js/components/UsersCreate.vue
<template>
  <form v-on:submit="saveForm()" class="p-form" autocomplete="off">
    <section class="p-section">
      <div class="p-section__header">
        <h3 class="p-section__heading">Create new User</h3>
      </div>
      <dl class="p-row">
        <dt>Name</dt>
        <dd><input type="text" v-model="user.name" class="c-input"></dd>
      </dl>
      <dl class="p-row">
        <dt>E-mail</dt>
        <dd><input type="text" v-model="user.email" class="c-input"></dd>
      </dl>
      <dl class="p-row">
        <dt>Password</dt>
        <dd><input type="password" v-model="user.password" class="c-input"></dd>
      </dl>
      <ul class="p-buttons">
        <router-link to="/" class="btn btn-default">Cancel</router-link>&nbsp;<button class="btn btn-success">Create</button>
      </ul>
    </section>
  </form>
</template>

<script>
  import http from '../http.js'
  export default {
    data: function () {
      return {
        user: {
          name: '',
          email: '',
          password: '',
        }
      }
    },
    methods: {
      saveForm() {
        var app = this;
        var newUser = app.user;
        http.post('/users',newUser,app.$router.push({path: '/'}));
      }
    }
  }
</script>
```

#### 7. UsersEdit.vueの作成(青枠)

```resources/assets/js/components/UsersEdit.vue
<template>
  <form v-on:submit="saveForm()" class="p-form">
    <section class="p-section">
      <div class="p-section__header">
        <h3 class="p-section__heading">Edit User</h3>
      </div>
      <dl class="p-row">
        <dt>Name</dt>
        <dd><input type="text" v-model="user.name" class="c-input"></dd>
      </dl>
      <dl class="p-row">
        <dt>E-mail</dt>
        <dd><input type="text" v-model="user.email" class="c-input"></dd>
      </dl>
      <ul class="p-buttons">
        <router-link to="/" class="btn btn-default">Cancel</router-link>&nbsp;<button class="btn btn-success">Edit</button>
      </ul>
    </section>
  </form>
</template>

<script>
  import http from '../http.js'
  export default {
    data() {
      return {
        user: {}
      }
    },
    mounted() {
      http.get('users/'+this.$route.params.id,result=>{
        this.user = result.data;
      });
    },
    methods: {
      saveForm() {
        http.put('users/'+this.$route.params.id,this.user,resp=>{
          alert("Edit successfully!");
          this.$router.push({path: '/'});
        })
      }
    }
  }
</script>
```

#### 8. 「npm run dev」コマンドの実行

```
$ npm run dev
 DONE  Compiled successfully in 4831ms                                                                                                                   17:51:31

       Asset     Size  Chunks                    Chunk Names
  /js/app.js  1.46 MB       0  [emitted]  [big]  /js/app
/css/app.css   196 kB    0, 0  [emitted]         /js/app, /js/app
```

これにより、「public/js/app.js」が作成されます。

### ユーザ管理アプリの起動
実装したユーザ管理アプリをテストします。いつものように「php artisan serve」を実行して開発用サービスを開始します。

#### 1. 「http://localhost:8000/home2 」にアクセスし、ログインします。

http://localhost:8000/home2 にアクセスするとログインしていない場合ログイン画面が表示されますのでログインします。

<img width="575" alt="スクリーンショット 2018-06-30 19.05.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/77bfbbf8-93eb-9845-c34c-f5ec0a0edfe2.png">

#### 2. ユーザリストが表示されます。

<img width="584" alt="スクリーンショット 2018-06-30 19.05.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/9bb8ee1c-9f63-eb50-2e6f-6824f98140a8.png">

#### 3. ユーザを作成します。
「Create new user」ボタンを押下して、ユーザ情報を入力して「Create」ボタンを押下します。

<img width="416" alt="スクリーンショット 2018-06-30 19.05.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/086accd6-99b1-983d-7734-bc83d0b0aa42.png">

ユーザが追加されてリスト表示されます。

<img width="608" alt="スクリーンショット 2018-06-30 19.13.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/e4b3bbc3-5fd4-4bf7-f167-2df74a19138f.png">

#### 4. ユーザ情報を編集します。

リストから編集したい行の「Edit」を押下して表示される「Edit User」画面でユーザ情報を編集し、「Edit」ボタンを押下します。

<img width="567" alt="スクリーンショット 2018-06-30 19.15.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/3383edb9-e2d4-55e7-d0e2-a0cf82953c52.png">

確認ダイアログが表示されますので、OKボタンを押下します。

<img width="570" alt="スクリーンショット 2018-06-30 19.15.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/49a35824-7e1a-6e2e-d654-c2c3a131d6ce.png">

編集された結果を反映したユーザ情報がリストされます。

<img width="576" alt="スクリーンショット 2018-06-30 19.15.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/256ac5b3-549a-9133-0674-2374bc743e75.png">

#### 5. ユーザ情報を削除します。

リストから削除したいユーザのDeleteボタンを押下すると、確認ダイアログが表示されるのでOKボタンを押下します。

<img width="572" alt="スクリーンショット 2018-06-30 19.16.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/bb9973a7-2520-8177-080d-ce4e80340f67.png">

削除が反映されて一覧が表示されます。

<img width="575" alt="スクリーンショット 2018-06-30 19.16.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/263562/aefbda4b-2dcc-8153-3765-b45b76e80539.png">

以上、「8. Vue.jsとAPIベースのユーザ管理アプリの追加」が完了です。

次は、「[9. Vue.jsとAPIベースのユーザ管理アプリへのペジネーション追加](https://qiita.com/sonrisa/items/d58c56b638924d8c6810)」です。