Laravel+Vue.js+vue-router+axiosでSPAによるModelの内容を出力

LaravelとVue.jsで自作のプロダクトを制作しています。
そこでVue.jsで表示する内容にLaravelのModelを表示させたSPAを作成したいと思います。

※制作途中のプロダクトで行ったので、Model・Controllerは事前に作成してあるものを使用いたしますので説明を省くところが多いです。ご了承下さい。

使用する主なソフトのバージョンは以下の通りです。

```バージョン
Laravel： 5.7
Vue.js： 2.5.17
node: 6.7.0
axios: 0.18
vue-router: 3.0.2
```

# LaravelとVue.jsの準備


###Laravel

ドットインストールで一通りの概要を理解し、
動画の内容を参考にして環境を準備しました。
プレミアム会員でしか動画を視聴できないのでご了承ください。

> ドットインストール
> https://dotinstall.com/lessons/basic_laravel_v2


###Vue.js
以下の記事を一通り行い、インストールしました。
わかりやすく、大変参考になりました。

> LaravelからVue.jsを使う最短レシピ
> https://qiita.com/fruitriin/items/e0f2c9aa035c3ff2c874


# axiosとvue-routerの準備

axiosによるHTTPリクエストで、登録されているModelのデータをajaxでjsonに変換して取得します。
また、vue-routerを使用することでSPA環境を作成することができます。

### vue-routerをインストール

package.jsonにインストールするvue-routerを記載します。
axiosはLaravelをインストールするとすでにパッケージがありますので、
package.jsonに記載されているならインストールの必要なしです。

```[Project名]/package.json
{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run development -- --watch",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.18",
        "bootstrap": "^4.0.0", 
        "cross-env": "^5.1",
        "jquery": "^3.2",
        "laravel-mix": "^4.0.15",
        "lodash": "^4.17.5",
        "popper.js": "^1.12",
        "resolve-url-loader": "^2.3.1",
        "sass": "^1.15.2",
        "sass-loader": "^7.1.0",
        "vue": "^2.5.17",
        "vue-template-compiler": "^2.6.8"    
    },
    "dependencies": {
        "vue-router": "^3.0.2"　//　追加
    }
}

```

端末でvue-routerをインストールします。

```端末
$ npm install vue-router
```

# vue-routerでSPAを作成



### ルーティング設定

以下の通り3つそれぞれにshow.blade.phpにアクセスできるようルーティングを設定します。
なぜかわかりませんが、stadiumを一番下にしないとshow.blade.phpにアクセスした時、自動的にstadiumのリンクを選択したままになりませんでした。

```[Project名]/route/web.php
Route::get('/stadium/{id}/address', 'StadiumPostsController@show')->where('id', '[0-9]+');
Route::get('/stadium/{id}/lat_lon', 'StadiumPostsController@show')->where('id', '[0-9]+');
Route::get('/stadium/{id}/stadium', 'StadiumPostsController@show')->where('id', '[0-9]+');
```

### コントローラ設定

Modelの内容を取得してViewの方にデータを表示させる処理を記述します。

```[Project名]/app/Http/Controllers/StadiumPostsController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\StadiumPost;

class StadiumPostsController extends Controller
{

    public function show($id) {
        $post = StadiumPost::findOrFail($id);
        return view('show')->with('stadium_post', $post);
    }
}
```

### リンク元のビューを作成
ブレードにrouter-linkの記述を作成します。
最終的には3つそれぞれ、「**スタジアムの名前**」「**スタジアムのある位置情報**」「**スタジアムの住所**」を Modelから取得できる様にします。
router-viewには最終的にModelの内容を表示できる様にします。

```[Project名]/resources/views/show.blade.php
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
  <head>
    <meta charset="utf-8">
　　　<meta name="csrf-token" content="{{ csrf_token() }}">
    <link rel="stylesheet" href="{{ mix('/css/app.css') }}">
    <link rel="stylesheet" href="/css/styles.css">
　</head>
　　　<body>
      <div class="container">
　　　<div id="app">
  　　　<nav>
    　　　<ul>
      　　　<router-link :to="{ name: 'stadium' }" exact>stadium</router-link>
      　　　<router-link :to="{ name: 'lat_lon' }">lat_lon</router-link>
      　　　<router-link :to="{ name: 'address' }">address</router-link>
    　　　</ul>
  　　　</nav>
  　　　<router-view></router-view>
 　　　</div>

　　　　<!-- 作成途中のソースコード -->

    </body>
  <script src="{{ mix('js/app.js') }}"></script>
</html>
```

### Vue.jsでvue-routerを使ったルーティング作成

リンク元にあった同じnameから相当のコンポーネントにあるテンプレートに推移します。
pathもルーティング設定と同じ様に設定します。

```[Project名]/resources/js/app.js
require('./bootstrap');
window.Vue = require('vue');
import VueRouter from 'vue-router';
import Stadium from './components/StadiumComponent.vue';
import Lat_lon from './components/Lat_lonComponent.vue';
import Address from './components/AddressComponent.vue';

Vue.use(VueRouter)

const routes = [
    { path: '/stadium/:id/stadium', name: 'stadium', component: Stadium},
    { path: '/stadium/:id/lat_lon', name: 'lat_lon', component: Lat_lon },
    { path: '/stadium/:id/address', name: 'address', component: Address },
];

const router = new VueRouter({
    mode: 'history',
    routes
});

const app = new Vue({
    router
}).$mount('#app');
```

### テンプレート内容の作成

`.vue`ファイルによってリンク元にあるrouter-view内にデータを表示します。

```[Project名]/resources/js/components/StadiumComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        Stadium
    </div>
</template>
```

```[Project名]/resources/js/components/Lat_lonComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        Lat_lon
    </div>
</template>
```

```[Project名]/resources/js/components/AddressComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        Address
    </div>
</template>
```

### CSS

リンクをクリックした時に文字を赤くします。わかりやすくするため実装しました。

```[Project名]/public/css/styles.css
.router-link-active {
    color: rgb(255, 0, 0);
}
```

### ここまでのLaravel+Vue.js+vue-router

URLから`/stadium/:id/stadium`にアクセスします。
以下の通り、リンクをクリックするとテンプレートの内容が表示されます。

![vue1.gif](https://qiita-image-store.s3.amazonaws.com/0/120727/9a5602e8-c856-dc60-74b7-f8b9e3010fbf.gif)

# Modelをjsonに変換する

端末上よりModelをjson形式に変換するルーティングとコントローラを作成します。

```[Project名]/route/web.php
Route::get('/stadium/{id}/address', 'StadiumPostsController@show')->where('id', '[0-9]+');
Route::get('/stadium/{id}/lat_lon', 'StadiumPostsController@show')->where('id', '[0-9]+');
Route::get('/stadium/{id}/stadium', 'StadiumPostsController@show')->where('id', '[0-9]+');

Route::get('ajax/stadium', 'Ajax\StadiumController@index');
```

```端末
$ php artisan make:controller Ajax\\StadiumController
```

コントローラには使用するModelであるStadiumPostを使うようにします。
このコントローラにアクセスすることで自動的にjson形式にしてくれます。

```[Project名]/app/Http/Controllers/Ajax/StadiumController.php
<?php

namespace App\Http\Controllers\Ajax;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class StadiumController extends Controller
{
    public function index() {
        return \App\StadiumPost::all();
    }
}
```

# axiosを使ったModel内容表示するSPA作成

`.vue`ファイルのテンプレートにv-forでModelのリストを表示します。
scriptにはaxiosによるHTTPリクエストでjson形式Modelデータのレスポンスを返します。
また、axiosは非同期処理を行い呼び出し元のビューに戻るので一度関数としては終了します。

```[Project名]/resources/js/components/StadiumComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        <li v-for="std in stadium">
            <span>{{ std.stadium }}</span>
        </li> 
    </div>
</template>
<script>
    export default {
        data() {
            return {
                stadium: {}
            }
        },
        mounted() {
            var self = this;
            var url = '/ajax/stadium';
            axios.get(url).then(function(response){
                self.stadium = response.data;
            });
        }
    }
</script>
```

```[Project名]/resources/js/components/Lat_lonComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        <li v-for="std in stadium">
            <span>{{ std.latitude }} , {{ std.longitude }}</span>
        </li>    
    </div>
</template>
<script>
    export default {
        data() {
            return {
                stadium: {}
            }
        },
        mounted() {
            var self = this;
            var url = '/ajax/stadium';
            axios.get(url).then(function(response){
                self.stadium = response.data;
            });
        }
    }
</script>
```

```[Project名]/resources/js/components/AddressComponent.vue
<template>
    <div class="col-md-8 col-md-offset-2">
        <li v-for="std in stadium">
            <span>{{ std.address }}</span>
        </li>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                stadium: {}
            }
        },
        mounted() {
            var self = this;
            var url = '/ajax/stadium';
            axios.get(url).then(function(response){
                self.stadium = response.data;
            });
        }
    }
</script>
```

### Laravel+Vue.js+vue-router+axiosによるModelデータ出力

以下の通り「**スタジアムの名前**」「**スタジアムのある位置情報**」「**スタジアムの住所**」を Modelから取得できるようになりました。

![vue2.gif](https://qiita-image-store.s3.amazonaws.com/0/120727/8844c2cc-0533-f515-1af4-3ed810155d38.gif)

# 参考

[Laravelとvueの連携！データベース内の一覧を表示する方法](https://blog.capilano-fw.com/?p=432)
[Laravel5.6とVue.jsで簡単なシングルページアプリケーション](https://qiita.com/shin1kt/items/8c98fb209de5caa9076d)
[【書籍】Vue.js入門 基礎から実践アプリケーション開発まで](https://www.amazon.co.jp/dp/B07J6FP6NQ/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)