Laravel, VueJSの連携・ルーティング(Web, VueRouter, API, VueRouterとAPIの連携)

#目的
LaravelとVueJSの開発において、
LaravelではWebとAPIのルーティング、
VueJSではVueRouter(Component)を使用してルーティングを行い、
LaravelとVueJSの機能を組み合わせて使用するときがあると思います。

それらのルーティングの設定が複雑なので、備忘録用にまとめました。

#前提
https://qiita.com/roughstorm/items/f9f86a7048c06c7d6544

で構築した環境を基にしています。


# Laravel Web Routing
```php:routes/web.php 
Route::get('/', function () {
	return view('welcome');
});
```
Laravelの初期設定の`routes/web.php`です。

まず、`Route::get`でHTTPのメソッドを指定しています。

今回は`Get`メソッドを指定しています。

`'/'`で、アクセスしてきたURLとの一致の条件を指定しています。
今回であれば、`http://localhost:8000/` が一致します。

`return view('welcome')`で `welcome.blade.php`を返します。 

`return view('[ビュー名]')`は,
`resources/views/`内の[ビュー名]に一致する`[ビュー名].blade.php`ファイルを返します。

流れとしては、`Get`メソッドで`http://localhost:8000/`にリクエストがきたら、
ルーティングの条件と一致した`welcome.blade.php`を返す、のような感じになると思います。

#本記事用の設定
```php:routes/web.php  
Route::get('/{any}', function() { 
    return view('app');
})->where('any', '.*');
```
`routes/web.php`を本記事のテスト用に上記の様に変更します。

この書き方では、`'/{any}'`と条件を指定し、
また、`)->where('any', '.*');`の句がついています。

`{any}`は、ルートパラメータと呼び、ルーティングの条件一致の際に使える変数の様なものと理解しています。
`->where`内で`'any', '.*'`と記述することで、アクセスしてきたURLに対して正規表現による条件一致が使用できます。

この記述では`'.*'`と正規表現の指定があるので、どのようなURLでアクセスしても、`app.blade.php`を返します。
`http://localhost:8000/.*`とも書けると思います。


もし、条件が一致しない場合、例えば正規表現に'a-Z'が設定されており、`http://localhost:8000/1`でアクセスした場合、
条件が一致しないので`return view('app')`で `app.blade.php`を返しません。

その後、他のルーティングの条件のいずれにも一致しなければ何も返さず、ブラウザ上で404が表示されます。

```php:resources/views/app.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <meta name="csrf-token" content="{{ csrf_token() }}">

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

    <link href="{{ mix('/css/app.css') }}" rel="stylesheet">
</head>
<body>

<div id="app">

</div>

<script src="{{ mix('/js/app.js') }}" defer></script>
</body>
</html>
```
`resources/views/app.blade.php`を上記の内容で新たに作成します。

# Vue Component Setting
```vue:recources/js/components/TestComponent.vue
<template>
    <div class="container">
      TEST
    </div>
</template>

<script>
    export default {}
</script>
```
まず読み込むコンポーネントの作成をします。


```js:resources/js/app.js
import TestComponent from "./components/TestComponent.vue";

require('./bootstrap');
window.Vue = require('vue');

Vue.component('test-component', TestComponent);

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

```

次に、`resources/js/app.js`を上記の様に記述します。
 
`import TestComponent from "./components/TestComponent.vue";`で、作成したコンポーネントを読み込みます。  

`require('./bootstrap');`と`window.Vue = require('vue');`でVueなどのJavascriptの依存関係を読み込んでいます。

`Vue.component('test-component', TestComponent);`でVueのコンポーネントを定義し、Laravel(php)から使えるようにします。

`const app = new Vue({});`で新しいVueのインスタンスを生成します。  
  
`el: '#app',`でインスタンスのAPIのid(名前)を設定します。  
ここで設定した名前(今回であればapp)が、Laravel(php)からコンポーネントを使用する際に紐づきます。


```php:resources/views/app.blade.php
<div id="app">
  <test-component></test-component>
</div>

<script src="{{ mix('/js/app.js') }}" defer></script>
```
`resources/views/app.blade.php`内の記述を上記の様に変更します。

`<script src="{{ mix('/js/app.js') }}" defer></script>`で`/js/app.js`を読み込みます。

`<div id="app"></div>`で`/js/app.js`で作成したVueインスタンスを設定します。  
今回は`el:`プロパティに`#app`を設定しているので、`<div id="app">`と指定します。  
`el:`プロパティが`#hoge`であれば、`<div id="hoge">`と指定します  

`<div id="app"></div>`内の、
`<test-component></test-component>`で`/js/app.js`で設定したコンポーネントを使用します。

#Vue Router Setting
```vue:recources/js/components/TestComponent.vue
<template>
    <div class="container">
      TEST
    </div>
</template>

<script>
    export default {}
</script>
```
まず読み込むコンポーネントの作成をします。(前項で作成したものをそのまま使っています。)


```js:resources/js/app.js
import VueRouter from 'vue-router';
import TestComponent from "./components/TestComponent.vue";

require('./bootstrap');

window.Vue = require('vue');

Vue.use(VueRouter);

const router = new VueRouter({

    mode: 'history',
    routes: [
        {
            path: '/test',
            name: 'test',
            component: TestComponent
        },
    ]


});

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


```
次に、`resources/js/app.js`内で、
`import VueRouter from 'vue-router';`  でVue-Routerを読み込み、
`import TestComponent from "./components/TestComponent.vue";`で作成したコンポーネントを読み込みます。 

`Vue.use(VueRouter);`で、Routerを使用する準備をします。

`const router = new VueRouter({});`で新しいVueRouterのインスタンスを生成します。

`mode: 'history',`でページ遷移時のURLの処理を設定します。三種類の設定値があります。
 
|値|動作|
|:-:|:-:|
|hash|ページ遷移時のURLに`http://localhost:8000/#/hoge`のような#が入る。|
|history|ページ遷移時のURLが`http://localhost:8000/hoge`のようになる(#が入らない。)。|
|abstarct|URLが変わらない。ページを遷移してもURLは`http://localhost:8000/`のまま。|

`path: '/test',`で、VueRouter内に設定したコンポーネントを呼び出すURLを設定します。
今回であれば,`http://localhost:8000/test`です。

`name: 'test',`で、VueRouter内に設定したコンポーネントを、
ほかのページから呼び出す際の名前を設定します。（名前付きルート）

今回であれば、` <router-link :to="{ name: 'test' }">test</router-link>`の記載で呼び出す（ページ遷移）ができます。

`component: TestComponent`で、作成したコンポーネントを読み込んでいます。

`el: '#app',`でインスタンスのAPIのid(名前)を設定します。  
`router`で、VueからRouterを使用する設定をしています。

```php:resources/views/app.blade.php
<div id="app">
  <router-view></router-view>
  <router-link v-bind:to="{name: 'test'}">Jump to Test</router-link>
</div>

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

`<router-view></router-view>`の記述よって、
`http://localhost:8000/test`にアクセスした際、`test`にルーティングされたコンポーネントを読み込みます。

`http://localhost:8000/test`にアクセスすると、`resources/js/app.js`のrouterの`path`に`/test`が設定されたコンポーネントを表示します。

`<router-link v-bind:to="{name: 'test'}">Jump to Test</router-link>`の記述によって、リンクをクリックした際に、`test`の名前がつけられたコンポーネントを表示します。

`http://localhost:8000/test`にアクセスするか、
`http://localhost:8000/`にアクセスし、Jump to Testをクリックすると、
TESTの文字列の表示が確認できると思います。

# Laravel API Routing
```powershell:powershell
php artisan make:controller TestController
```
Dockerに構築したローカルサーバー上で`php artisan make:controller TestController`を実行します。

```php:\app\Http\Controllers\TestController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;

class TestController extends Controller
{
    public function index()
    {
        $data = [
            'test'=>'Test data from controller',
        ];
        return view('app', $data);
    }
}


```
`\app\Http\Controllers\`に`TestController.php`が作成されます。
上記の様に記述を変更します。

`namespace App\Http\Controllers;`で名前空間を指定しています。
名前空間は、クラス名などが重複しないために、このクラスはこの名前空間のもの、といった定義をするためのものです。

`use ~~`で指定した名前空間にあるクラスなどを読み込んでいます。
今回は、`Illuminate`下にある`\Http\Request`と`\Support\Facades\View`を読み込んでいます。

`class TestController extends Controller{~}`で`TestController`クラスを定義し、かつ`extends`で`Controller`クラスを継承しています。
`Controller`クラスはLaravelに最初からあるもので、ミドルウェアの登録などをやってくれています。

`return view('app', $data);`にて`app.blade.php`に戻り、かつ`$data`の配列を渡します。

```php:\routes\api.php
<?php
Route::get('/test', 'TestController@index');
```
`\routes\`の`api.php`を上記の様に変更します。
このファイルで、LaravelのAPIのルーティングの設定をします。

`Route::get('/test',`の記述は、`routes/web.php`と同じです。

***また、`api.php`で設定したルーティングは`http://localhost:8000/api/(設定したパス)`として設定されます。***

今回は'/test'を設定していますので、実行する際は`http://localhost:8000/api/test`にアクセスします。

`'TestController@index'`の`TestController`で呼び出したいコントローラークラスの名前を指定し、
`index`で呼び出し時に実行したい関数名を指定します。

```php:resources/views/app.blade.php
<div id="app">
</div>

<p>
    Received:{{ $test }}
</p>
```
`app.blade.php`に`{{ $test }}`と記述すると、
`TestController.php`から送られてきた配列(`$data`)の中の、
キー(`test`)が一致する値(`Test data from controller`)を表示します。

この変数はVueJSとは関係ないので、`<div id="app"></div>`内に記述する必要はありません。

`http://localhost:8000/api/test`にアクセスし、動作が確認できると思います。


# Laravel(API)からデータを受け取りVueで描画
Laravelは、MVCモデルに基づいて構成されたフレームワークだと思いますので、
そちらにのっとったような感じでやっていきたいと思います。

# テストデータの作成・設定
```powershell:powershell
php artisan make:migration create_tests_table
```
Dockerに構築したローカルサーバー上で`php artisan make:migration create_tests_table`を実行します。

:::note info
Laravelでは、モデルクラス内で明示的にテーブル名を指定しない限り、
モデルのクラス名にsを合わせた名前が指定されるテーブル名になります。

今回はTestの名前でモデルを作りますので、テーブル名は自動的にtestsが指定されます。
ゆえにそれに合わせる形でtestsの名前でテーブルを作成しています。

:::

https://qiita.com/janet_parker/items/6f6c8561f201fdcbcdb0

```php:\database\migrations\YYYY_MM_DD_[0-9]{6,6}_create_tests_table.php
<?php

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

class CreateTestTable extends Migration
{
    public function up()
    {
        Schema::create('tests', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title', 100);
            $table->string('content', 100);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('tests');
    }
}

```
`database\migrations\`に`YYYY_MM_DD_[0-9]{6,6}_create_test_table.php`が作成されますので、上記の様に内容を変更します。

`Schema::create('test',`の`'test'`で、作成するテーブル名を指定します。

`$table->`でテーブル内に作成するカラムを指定します。

`bigIncrements('id');`は、`id`というカラムを、Bigint型で自動で増分(1->2->3...)していくように設定しています。

`string('title', 100);`は、String型で100(文字？バイト？)と指定しています。

`timestamps();`は、データが作成、更新された年月日時分秒(Datetime)を指定しています。


```powershell:powershell
php artisan make:model Test
```
Dockerに構築したローカルサーバー上で`php artisan make:model Test`を実行します。

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

namespace App;

use Illuminate\Database\Eloquent\Model;

class Test extends Model
{
    protected $fillable = [
        'title',
        'content',
    ];
}
```
`app/`に`Test.php`が作成されますので、上記の様に内容を変更します。

Laravelでは、Eloquentというものでデータベースの操作ができます。
データの操作をする前に、モデルに対してfillableかguardedという属性をつけないといけない様です。

今回は、`protected $fillable`でfillableという属性をつけ、
`title`と`content`のカラムを編集していいよ、といった設定をしています。

`guarded`をつけると、逆に`title`と`content`のカラムが編集できないようになる様です。

```powershell:powershell
php artisan make:seeder TestTableSeeder
```
Dockerに構築したローカルサーバー上で`php artisan make:seeder TestTableSeeder`を実行します。

```php:database\seeds\TestTableSeeder.php
<?php
use App\Test;
use Illuminate\Database\Seeder;

class TestTableSeeder extends Seeder
{
    public function run()
    {
        for ($i = 1; $i <= 3; $i++) {
            Test::create([
                'title' => 'title' . $i,
                'content' => 'content' . $i,
            ]);
        }
    }
}
```
`database\seeds\`に`TestTableSeeder.php`が作成されますので、上記の様に内容を変更します。

For文を回し、`Task::create([`で生成したテストデータをデータベース上の`test`テーブルに格納します。

```php:database/seeds/DatabaseSeeder.php
 public function run()
 {
  $this->call(TestTableSeeder::class);
 }
```
`database/seeds/DatabaseSeeder.php`を上記の様に変更します。

`$this->call(TestTableSeeder::class);`で、先ほど作成した`TestTableSeeder.php`を呼ぶようにします。

```powershell:powershell
php artisan migrate --seed
```
migrateコマンドを実行します。

```powershell:powershell
php artisan tinker
Test::all();
```
`php artisan tinker`で、Laravel搭載のデバッグ用の対話シェルであるtinkerを起動します。
tinker内で`Test::all();`を実行することで、migrationによって作成されたテーブルに挿入されたデータが確認できると思います。

# LaravelとVueの連携
```php:\app\Http\Controllers\TestController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use App\Test;

class TestController extends Controller
{
    public function index()
    {
        $data = [
            'test'=>'Test data from controller',
        ];
        return view('app', $data);
    }
    
    public function getAllRecords()
    {
        return Test::all();
    }
}

```
`\app\Http\Controllers\TestController.php`に`getAllRecords`関数を追加します。
`Test::all();`で、取得したtestsテーブル内の全レコードを、`return`で返します。

```php:\routes\api.php
<?php
Route::get('/test', 'TestController@getAllRecords');
```
`\routes\api.php`を上記の様に変更して、
`http://localhost:8000/api/test`で作動する関数を`getAllRecords`に変更します。

```vue:\resources\js\components\TestComponent.vue
<template>
    <div class="container">
        <table>
            <tbody>
            <tr v-for="test in tests">
                <th scope="row">{{ test.id }}</th>
                <td>{{ test.title }}</td>
                <td>{{ test.content }}</td>
            </tr>
            </tbody>
        </table>
    </div>
</template>

<script>
    export default {

        data: function () {
            return {
                tests: []
            }
        },
        
        methods: {
            getAllRecords() {
                
                axios.get('/api/test')

                .then((res) => {
                    this.tests= res.data;
                 });
            }
        },

        mounted() {
            this.getAllRecords();
        }

    }
</script>

```
`\resources\js\components\TestComponent.vue`を上記の様に変更します。
書き方上、`<template></template>`から書いていますが、実際に先に動くのは`<script></script>`です。

`data: function`で`tests`の配列を定義しています。
この`tests`がtemplate内で読み込むときの名前になります。

`methods:`に`getAllRecords()`という関数を定義します。
`axios.get('/api/test')`で、`/api/test`のパスに対してGetメソッドでリクエストを投げています。

`.then((res) => {~});`に``axios.get`が成功した際の処理を書いています。

`this.tests= res.data;`で`res`(HTTPレスポンス)の中の`data`を`tests`に代入しています。

流れでいうと、`/api/test`のパスに対してGetメソッドでリクエストを投げ、
`routes/api.php`の`test`に指定されたクラスの関数を実行し、
成功した場合、実行の結果(testsテーブルの全レコード)が`res`に格納され、
`test`の中に実行の結果(testsテーブルの全レコード)が代入される、という感じです。

`mounted() {~}`で、DOMが作成された直後の処理を定義しています。
`this.getAllRecords();`を記述しているので、このクラスの`getAllRecords()`を実行します。

```php:resources/views/app.blade.php
<div id="app">
    <router-view></router-view>
</div>
```
VueRouterの動作確認用に、
`resources/views/app.blade.php`の`<div id="app"></div>`に`<router-view></router-view>`を記述してください。

# 動作確認
ソースをビルド`npm run dev`してから、`http://localhost:8000/test`にアクセスします。
 ***http://localhost:8000/api/test***　ではありません。
http://localhost:8000/api/testにアクセスすると、Json形式のレコードデータが見れると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286510/aa5a28bc-9c03-ac22-aa44-7db49d9424b2.png)
こんな感じで動作が確認できると思います。

#　おわりに
複雑だな～、と思って軽くまとめようとおもったら激重な内容になりました。
複雑すぎる。