Nuxt.jsとLaravelを同じリポジトリで管理し、Valetで動かすまで

表題の通りです。
Nuxt.jsやLaravel、Valetが何なのかは省略。
やりたいこととしては、

- Nuxt.jsとLaravelは同じディレクトリ(リポジトリ)で共存させる。
- Nuxt.jsはSPAとする。
- LaravelはAPI専用とする。
- Nuxt.jsからLaravelのAPIを呼び出してレスポンスを表示する。
- 最終的に両方ともLaravel Valetで動かせるようにする。

# インストール

まずは公式ドキュメントを参考にValetのインストール。

https://laravel.com/docs/5.6/valet

Valetで管理するディレクトリ以下にLaravelをインストールする。
バージョンは5.6
プロジェクト名は`laranuxt`とする。

```
$ composer create-project --prefer-dist laravel/laravel laranuxt
```

Valetが動いているのですぐに表示できる。
ドメインが`.test`となっているのはValetの設定を変えたため(自分いつ変えたんだ:thinking:)

![スクリーンショット 2018-04-24 18.26.24.png](https://qiita-image-store.s3.amazonaws.com/0/123511/186a6e8f-4581-ea19-db7b-a6b44c304175.png)

続いてNuxt.jsのインストール。
今回はLaravelで作られた`resources`ディレクトリの下にインストールする。
インストール先のディレクトリ名は`nuxt`とする。

https://ja.nuxtjs.org/guide/installation

```
$ cd /path/to/laranuxt/resources
$ vue init nuxt-community/starter-template nuxt
$ cd nuxt
$ npm install
$ npm run dev
```

![スクリーンショット 2018-04-24 18.49.20.png](https://qiita-image-store.s3.amazonaws.com/0/123511/81772116-f895-ef95-2a4c-ae10b286922c.png)

これはValetではないことに注意。

# Nuxt.jsでシンプルな画面を作ってみる

Nuxt.jsとLaravelを組み合わせる前にNuxt.jsでシンプルな画面遷移を作ってみる。
作成する`pages`ディレクトリの構造を以下に示す。

```
pages/
--| message/
-----| hello.vue
-----| goodbye.vue
--| index.vue
```

```pages/index.vue
<template>
  <section class="container">
    <div>
      <app-logo/>
      <h1 class="title">
        LaraNuxt
      </h1>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green">Documentation</a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey">GitHub</a>
      </div>
      <div class="links">
        <a
          href="message/hello"
          class="button--green">Hello</a>
        <a
          href="message/goodbye"
          class="button--grey">Good bye</a>
      </div>
    </div>
  </section>
</template>

<script>
import AppLogo from '~/components/AppLogo.vue'

export default {
  components: {
    AppLogo
  }
}
</script>
```

```pages/message/hello.vue
<template>
  <section class="container">
    <div>
      <h1 class="title">
        Hello!
      </h1>
      <div class="links">
        <a
          href="../"
          class="button--green">Nuxt Top</a>
      </div>
    </div>
  </section>
</template>

<script>
</script>
```

```pages/message/goodbye.vue
<template>
  <section class="container">
    <div>
      <h1 class="title">
        Good bye!
      </h1>
      <div class="links">
        <a
          href="../"
          class="button--green">Nuxt Top</a>
      </div>
    </div>
  </section>
</template>

<script>
</script>
```

![スクリーンショット 2018-04-24 19.03.51.png](https://qiita-image-store.s3.amazonaws.com/0/123511/5302205a-739b-cc1b-ce3c-bf7072613901.png)
![スクリーンショット 2018-04-24 19.04.02.png](https://qiita-image-store.s3.amazonaws.com/0/123511/0b68331a-7b85-535b-802b-6bd16aae42f5.png)

# Nuxt.jsとLaravelの合体

最初にNuxt.jsをSPAとしてビルドする。
`nuxt.config.js`に`mode: 'spa'`を追加してビルド。

```
$ npm run build
```

これで公開用の`dist`ディレクトリが作成される。
続いてLaravelの`public`ディレクトリ以下に`dist`へのシンボリックリンクを作成する。

```
$ ln -s /path/to/laranuxt/resources/nuxt/dist /path/to/laranuxt/public/nuxt
```

Valetからアクセスできるか。

![スクリーンショット 2018-04-24 19.14.02.png](https://qiita-image-store.s3.amazonaws.com/0/123511/08c03b84-5f6f-5e36-8544-25d3c19aa829.png)

だめでした:sob:

そこで、`/nuxt/`以下はNuxt.jsのファイルを読み込めるよう、Valetのドライバーを作る。
以下を参考。

https://laracasts.com/discuss/channels/servers/possible-to-use-htaccess-files-using-valet
https://gist.github.com/jakecobley/366ae401f44f53bbaa65dcd7371050c2

`/nuxt/`以下へのリクエストで静的ファイルが存在しないものはすべて`/public/nuxt/index.html`を読み込むようにする。

```
$ cp ~/.valet/Drivers/SampleValetDriver.php ~/.valet/Drivers/NuxtValetDriver.php
```

```NuxtValetDriver.php
<?php

class NuxtValetDriver extends ValetDriver
{
    /**
     * Determine if the driver serves the request.
     *
     * @param  string  $sitePath
     * @param  string  $siteName
     * @param  string  $uri
     * @return bool
     */
    public function serves($sitePath, $siteName, $uri)
    {
        if (strpos($uri, 'nuxt') !== false) {
            return true;
        }

        return false;
    }

    /**
     * Determine if the incoming request is for a static file.
     *
     * @param  string  $sitePath
     * @param  string  $siteName
     * @param  string  $uri
     * @return string|false
     */
    public function isStaticFile($sitePath, $siteName, $uri)
    {
        $staticFilePath = $sitePath.'/public'.$uri;
        if (file_exists($staticFilePath)
            && !is_dir($staticFilePath)
            && pathinfo($staticFilePath)['extension'] != 'php') {
            return $staticFilePath;
        }

        return false;
    }

    /**
     * Get the fully resolved path to the application's front controller.
     *
     * @param  string  $sitePath
     * @param  string  $siteName
     * @param  string  $uri
     * @return string
     */
    public function frontControllerPath($sitePath, $siteName, $uri)
    {
        return $sitePath.'/public/nuxt/index.html';
    }
}
```

もうひとつやらなければいけないことは、Nuxt.jsのベースURLを変更すること。

https://ja.nuxtjs.org/api/configuration-router

`nuxt.config.js`に`router: {base: '/nuxt/'}`を追加してビルド。

果たして、、、

![スクリーンショット 2018-04-24 19.31.05.png](https://qiita-image-store.s3.amazonaws.com/0/123511/510ec967-79f6-7ba1-a6c0-95ecab1fa2b7.png)

成功！！:joy::joy::joy:
ドメインを見ると確かにValetです。

![スクリーンショット 2018-04-24 19.32.29.png](https://qiita-image-store.s3.amazonaws.com/0/123511/a629f61f-2e69-c007-7c8a-bb0b778062de.png)

画面遷移も成功。

![スクリーンショット 2018-04-24 19.33.19.png](https://qiita-image-store.s3.amazonaws.com/0/123511/bd016e2a-ba7d-4756-162c-554330ca8470.png)

それでいてトップページはLaravel。

# LaravelでAPI作成

`routes/api.php`に文字列を返すだけのシンプルなAPIを作成する。

```routes/api.php
<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

Route::get('hello', function () {
    return 'Hello! from Laravel';
});

Route::get('goodbye', function () {
    return 'Good bye! from Laravel';
});
```

![スクリーンショット 2018-04-24 19.37.43.png](https://qiita-image-store.s3.amazonaws.com/0/123511/bd6356da-bbd8-0e36-3c37-3eec7294b447.png)

# Nuxt.jsからLaravelのAPIを呼んでレスポンスを表示

Nuxt.jsにaxiosをインストール

```
$ cd /path/to/laranuxt/resources/nuxt
$ npm install --save axios
```

`nuxt.config.js`に`vendor`を追加。

```nuxt.config.js
module.exports = {

  ....

  build: {
    vendor: ['axios']
  }

  ....
}
```

```pages/message/hello.vue
<template>
  <section class="container">
    <div>
      <h1 class="title">
        {{ message }}
      </h1>
      <div class="links">
        <a
          href="../"
          class="button--green">Nuxt Top</a>
      </div>
    </div>
  </section>
</template>

<script>
import axios from 'axios'
export default {
  asyncData () {
    return axios.get('/api/hello')
    .then((msg) => {
      return { message: msg.data }
    })
  }
}
</script>
```

```pages/message/goodbye.vue
<template>
  <section class="container">
    <div>
      <h1 class="title">
        {{ message }}
      </h1>
      <div class="links">
        <a
          href="../"
          class="button--green">Nuxt Top</a>
      </div>
    </div>
  </section>
</template>

<script>
import axios from 'axios'
export default {
  asyncData () {
    return axios.get('/api/goodbye')
    .then((msg) => {
      return { message: msg.data }
    })
  }
}
</script>
```

そしてビルド。

![スクリーンショット 2018-04-24 19.48.16.png](https://qiita-image-store.s3.amazonaws.com/0/123511/1ddcaec3-fba4-aeb9-21f9-6f332295231d.png)

できた〜〜:joy:

# まとめ

正直なところ、シンプルな構成にしたいならNuxt.jsとLaravelを同じリポジトリにしないで別々に管理したほうがよいと思う(身も蓋もなし:joy:)

もしかしたらもっと楽な方法があるのかもしれないけど、今回Valetで確認するためにいちいちNuxt.jsをビルドするのが面倒だった(watch and build的なのがあるのかな)。
手軽に確認するために`npm run dev`を使おうとしたが、そうするとAPIとの通信においてCORSで引っかかってしまう。
このあたりどうしたものか。

# 参考
https://ja.nuxtjs.org/
https://laracasts.com/discuss/channels/servers/possible-to-use-htaccess-files-using-valet
https://gist.github.com/jakecobley/366ae401f44f53bbaa65dcd7371050c2
http://techblog.scouter.co.jp/entry/2017/11/02/120748
https://qiita.com/mikakane/items/a76b230d91dc5ae80574