Laravel 8でInertia+Vuetifyを使ってみる - Laravel JetStream - アタック
はじめに
経緯
Laravel 6で組んでいるAPIサーバーをLaravel 8で組みなおしてみるという一連の実証です。
以前の、Laravel 8のAPI認証を味見するのとおり、APIサーバーとしてSanctumのAPI認証を試してみました。
この仕組みは、Vuetifyを利用したSPAとの連携を想定した仕組みです。
(ちなみに、SPA認証を利用しないのは、SPA以外からも連携する想定があるから、です。)
そんな折、こちらの方の記事が目に留まりました。(感謝)
Laravel JetStreamのinertiaで、Vuetifyが使えるですって?!
ということで、試します。
Laravel JetStreamについて
公式のドキュメントLaravel Jetstream 1.0が用意されています。
ログイン、ユーザー登録、メール確認、2要素認証、セッション管理などなど、大体のWEBアプリケーションで必要となる仕組みのパッケージ的なものです。
Laravel 6では、
composer require laravel/ui:^1.0
からの、
php artisan ui vue --auth
などとすることで、同等のことが行えました。(vueの箇所はタイプで、bootstrap, vue, react の3タイプを指定可能)
このあたりの仕組みがJetStreamに置き換えられているとのことです。
環境
環境は、こちらの記事と同等のものを利用するべく、(2) Laravelのプロジェクト作成 以降を実施して新しい環境を作成します。
環境構築手順:基本
プロジェクト作成
※詳細は、こちらの記事を参照
-
create project
laradock@9760cc7de2d2:/var/www# composer create-project laravel/laravel example-app02 "8.*"
laradock@9760cc7de2d2:/var/www$ vi example-app02/.env
APP_URL=http://localhost ↓ APP_URL=http://localhost/app02 ASSET_URL=/app02
※その他も調整
※必要に応じてexample-app02用のデータベースも新規作成(ここではdefaultデータベースをmigrate:refreshして利用する想定) -
Laravelプロジェクト公開ディレクトリの準備
laradock@9760cc7de2d2:/var/www# cd public/ laradock@9760cc7de2d2:/var/www/public# ln -s ../example-app02/public/ app02
-
nginxのdefault.conf設定ファイルの修正
/var/www/laradock/nginx/sites/default.conflocation /app02/ { try_files $uri $uri/ /app02/index.php$is_args$args; }
Dockerホスト側でnginxを再起動
laradock> docker-compose restart nginx Restarting laradock_nginx_1 ... done
この時点で、http://localhost/app02/
にブラウザアクセスするとLaravelのWelcome画面が表示される。
Laravel JetStreamをインストール
-
プロジェクトフォルダに移動し、Composerでlaravel/jetstreamを追加
laradock@9760cc7de2d2:/var/www$ cd example-app02 laradock@9760cc7de2d2:/var/www/example-app02$ composer require laravel/jetstream
実行イメージ
Using version ^2.4 for laravel/jetstream ./composer.json has been updated Running composer update laravel/jetstream Loading composer repositories with package information Updating dependencies Lock file operations: 9 installs, 0 updates, 0 removals - Locking bacon/bacon-qr-code (2.0.4) - Locking dasprid/enum (1.0.3) - Locking jaybizzle/crawler-detect (v1.2.106) - Locking jenssegers/agent (v2.6.4) - Locking laravel/fortify (v1.8.2) - Locking laravel/jetstream (v2.4.1) - Locking mobiledetect/mobiledetectlib (2.8.37) - Locking paragonie/constant_time_encoding (v2.4.0) - Locking pragmarx/google2fa (8.0.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 9 installs, 0 updates, 0 removals - Downloading dasprid/enum (1.0.3) - Downloading bacon/bacon-qr-code (2.0.4) - Downloading jaybizzle/crawler-detect (v1.2.106) - Downloading paragonie/constant_time_encoding (v2.4.0) - Downloading pragmarx/google2fa (8.0.0) - Downloading laravel/fortify (v1.8.2) - Downloading mobiledetect/mobiledetectlib (2.8.37) - Downloading jenssegers/agent (v2.6.4) - Downloading laravel/jetstream (v2.4.1) - Installing dasprid/enum (1.0.3): Extracting archive - Installing bacon/bacon-qr-code (2.0.4): Extracting archive - Installing jaybizzle/crawler-detect (v1.2.106): Extracting archive - Installing paragonie/constant_time_encoding (v2.4.0): Extracting archive - Installing pragmarx/google2fa (8.0.0): Extracting archive - Installing laravel/fortify (v1.8.2): Extracting archive - Installing mobiledetect/mobiledetectlib (2.8.37): Extracting archive - Installing jenssegers/agent (v2.6.4): Extracting archive - Installing laravel/jetstream (v2.4.1): Extracting archive 1 package suggestions were added by new dependencies, use `composer suggest` to see details. Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fruitcake/laravel-cors Discovered Package: jenssegers/agent Discovered Package: laravel/fortify Discovered Package: laravel/jetstream Discovered Package: laravel/sail Discovered Package: laravel/sanctum Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. 78 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi No publishable resources for tag [laravel-assets]. Publishing complete.
Vue 2を適用するため、composer.jsonでバージョンを調整しアップデート(ダウングレード)
composer.json"laravel/jetstream": "^2.4", ↓ "laravel/jetstream": "2.1.4",
※
^2.1.4
のように^
をつけるとダウングレードできないようなので注意laradock@9760cc7de2d2:/var/www/example-app02$ composer update
実行イメージ
Loading composer repositories with package information Updating dependencies Lock file operations: 0 installs, 2 updates, 4 removals - Removing dflydev/dot-access-data (v3.0.1) - Removing league/config (v1.1.1) - Removing nette/schema (v1.2.1) - Removing nette/utils (v3.2.5) - Downgrading laravel/jetstream (v2.4.2 => v2.1.4) - Downgrading league/commonmark (2.0.2 => 1.6.6) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 0 installs, 2 updates, 4 removals - Removing nette/utils (v3.2.5) - Removing nette/schema (v1.2.1) - Removing league/config (v1.1.1) - Removing dflydev/dot-access-data (v3.0.1) - Downgrading league/commonmark (2.0.2 => 1.6.6): Extracting archive - Downgrading laravel/jetstream (v2.4.2 => v2.1.4): Extracting archive Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fruitcake/laravel-cors Discovered Package: jenssegers/agent Discovered Package: laravel/fortify Discovered Package: laravel/jetstream Discovered Package: laravel/sail Discovered Package: laravel/sanctum Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. 77 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi No publishable resources for tag [laravel-assets]. Publishing complete.
-
Inertiaと同時にJetstreamをインストール
Inertiaを指定してインストール ※チーム機能を含める(お好みで)laradock@9760cc7de2d2:/var/www/example-app02$ php artisan jetstream:install inertia --teams
実行イメージ
laradock@9760cc7de2d2:/var/www/example-app02$ php artisan jetstream:install inertia --teams Migration created successfully! ./composer.json has been updated Running composer update inertiajs/inertia-laravel laravel/sanctum tightenco/ziggy Loading composer repositories with package information Updating dependencies Lock file operations: 2 installs, 0 updates, 0 removals - Locking inertiajs/inertia-laravel (v0.3.6) - Locking tightenco/ziggy (v1.4.1) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 2 installs, 0 updates, 0 removals - Downloading inertiajs/inertia-laravel (v0.3.6) 0/1 [>---------------------------] 0% 1/1 [============================] 100% - Installing inertiajs/inertia-laravel (v0.3.6): Extracting archive - Installing tightenco/ziggy (v1.4.1): Extracting archive 0/2 [>---------------------------] 0% 2/2 [============================] 100% Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fruitcake/laravel-cors Discovered Package: inertiajs/inertia-laravel Discovered Package: jenssegers/agent Discovered Package: laravel/fortify Discovered Package: laravel/jetstream Discovered Package: laravel/sail Discovered Package: laravel/sanctum Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Discovered Package: tightenco/ziggy Package manifest generated successfully. 78 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi No publishable resources for tag [laravel-assets]. Publishing complete. Copied Directory [/vendor/laravel/sanctum/database/migrations] To [/database/migrations] Copied File [/vendor/laravel/sanctum/config/sanctum.php] To [/config/sanctum.php] Publishing complete. Middleware created successfully. Inertia scaffolding installed successfully. Please execute "npm install && npm run dev" to build your assets.
-
インストールを完了する
パッケージをインストールlaradock@9760cc7de2d2:/var/www/example-app02$ npm install
実行イメージ
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. added 835 packages, and audited 836 packages in 26s 92 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
参考 作業後のpackage.json
package.json{ "private": true, "scripts": { "dev": "npm run development", "development": "mix", "watch": "mix watch", "watch-poll": "mix watch -- --watch-options-poll=1000", "hot": "mix watch --hot", "prod": "npm run production", "production": "mix --production" }, "devDependencies": { "@inertiajs/inertia": "^0.8.2", "@inertiajs/inertia-vue": "^0.5.4", "@tailwindcss/forms": "^0.2.1", "@tailwindcss/typography": "^0.3.0", "autoprefixer": "^10.0.2", "axios": "^0.21", "laravel-mix": "^6.0.6", "lodash": "^4.17.19", "portal-vue": "^2.1.7", "postcss": "^8.1.14", "postcss-import": "^12.0.1", "tailwindcss": "^2.0.1", "vue": "^2.5.17", "vue-loader": "^15.9.6", "vue-template-compiler": "^2.6.10" } }
ビルド
laradock@9760cc7de2d2:/var/www/example-app02$ npm run dev
実行イメージ
✔ Mix Compiled successfully in 7.22s Laravel Mix v6.0.31 ✔ Compiled Successfully in 7068ms ┌──────────────────────────────────────┬──────────┐ │ File │ Size │ ├──────────────────────────────────────┼──────────┤ │ /js/app.js │ 2.2 MiB │ │ css/app.css │ 42.7 KiB │ └──────────────────────────────────────┴──────────┘ webpack compiled successfully
-
マイグレーション
laradock@9760cc7de2d2:/var/www/example-app02$ php artisan migrate
※必要に応じて migrate:resetの実行や、migrate:freshで実行
Inertiaのセットアップ
初期設定を実施
laradock@9760cc7de2d2:/var/www/example-app02$ php artisan vendor:publish --tag=jetstream-views
Copied Directory [/vendor/laravel/jetstream/resources/views] To [/resources/views/vendor/jetstream]
Publishing complete.
再度ビルド
laradock@9760cc7de2d2:/var/www/example-app02$ npm run dev
サブディレクトリ設置の調整
app.bladeのパスを調整
:
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
↓
<link rel="stylesheet" href="{{ asset(mix('css/app.css')) }}">
:
<script src="{{ mix('js/app.js') }}" defer></script>
↓
<script src="{{ asset(mix('js/app.js')) }}" defer></script>
:
fortify.phpのホームを修正
'home' => RouteServiceProvider::HOME,
↓
'home' => config('app.url') . RouteServiceProvider::HOME,
ログアウト後のリダイレクト先を設定
:
use Laravel\Fortify\Contracts\LogoutResponse;
:
public function register()
{
$this->app->instance(LogoutResponse::class, new class implements LogoutResponse {
public function toResponse($request)
{
return redirect('/login');
}
});
}
:
※参考:Laravel 8のログアウト後のリダイレクト先を設定 - Laravel Fortify -
Welcome.vueのDashboard
リンクのhref
の設定を修正
<inertia-link v-if="$page.props.user" href="/dashboard" class="text-sm text-gray-700 underline">
↓
<inertia-link v-if="$page.props.user" :href="route('dashboard')" class="text-sm text-gray-700 underline">
※v-bind
ディレクティブ省略記述の:
を忘れないように。
ここまでで手順に問題が無ければ、http://localhost/app02/
でLaravelのWelcome画面が表示される。
右上にLogin
とRegister
のリンクが表示され、かつ、リンク先の画面が正しく表示されれば、JetStream Inertiaのインストール成功。
参考:動作確認
動作確認をするのであれば、Register
のリンクから登録画面にアクセスし、Name、Email、Passwordを入力してREGISTERするとよい。
- Seederでデータ登録する場合、Userレコードと共にTeamレコードも登録する必要がある。
- 登録後、phpMyAdminなどで、該当ユーザーのemail_verified_atに現在時刻を直接入力し、メールアドレスの確認を手動で完了にすると
Route::middleware(['verify'])
を通過できる。
環境構築手順:Vuetify関連
続いてVuetifyの導入を行う。
Vuetifyのインストール
依存モジュールとVuetifyをインストール
詳細は公式ドキュメントを参照。こちらを参考に1つ1つ作業実施。
1. 本体インストール
laradock@9760cc7de2d2:/var/www/example-app02$ npm install vuetify
2. 依存パッケージインストール
laradock@9760cc7de2d2:/var/www/example-app02$ npm install sass@~1.32 sass-loader deepmerge -D
3. フォントのインストール(簡易)
app.blade.phpの<!-- Fonts -->
あたりにCDNのフォントリソースリンクを追加
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
フォントに関する詳細は公式ドキュメントIcon Fontsを参照
4. plugins/vuetify.jsを設置
resources/js
にplugins
フォルダを作成、以下の内容でvuetify.js
を作成
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
Vue.use(Vuetify)
const opts = {}
export default new Vuetify(opts)
5.app.jsの調整
app.jsのimport群に以下のimportを追加
import vuetify from './plugins/vuetify';
app.jsのnew Vue()
に、vuetify
適用
:
new Vue({
render: (h) =>
↓
new Vue({
vuetify,
render: (h) =>
:
app.js 全文
require('./bootstrap');
// Import modules...
import Vue from 'vue';
import { App as InertiaApp, plugin as InertiaPlugin } from '@inertiajs/inertia-vue';
import PortalVue from 'portal-vue';
import vuetify from './plugins/vuetify';
Vue.mixin({ methods: { route } });
Vue.use(InertiaPlugin);
Vue.use(PortalVue);
const app = document.getElementById('app');
new Vue({
vuetify,
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
},
}),
}).$mount(app);
Vuetify Laravel Mix Extensionの導入
インストール
Vuetify Laravel Mix Extensionをインストール
laradock@9760cc7de2d2:/var/www/example-app02$ npm install vuetifyjs-mix-extension@0.0.20 -D
vuetify-loaderをインストール
laradock@9760cc7de2d2:/var/www/example-app02$ npm install vuetify-loader -D
webpack.mix.jsにvuetifyjs-mix-extensionの読み込みを追加
require('vuetifyjs-mix-extension');
vuetifyjs-mix-extensionを有効化
mix.js('resources/js/app.js', 'public/js').vue()
↓
mix.js('resources/js/app.js', 'public/js').vuetify('vuetify-loader').vue()
webpack.mix.js 全文
const mix = require('laravel-mix');
require('vuetifyjs-mix-extension');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel applications. By default, we are compiling the CSS
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js').vuetify('vuetify-loader').vue()
.postCss('resources/css/app.css', 'public/css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
])
.webpackConfig(require('./webpack.config'));
if (mix.inProduction()) {
mix.version();
}
反映
laradock@9760cc7de2d2:/var/www/example-app02$ npm run dev
※npm run watch
でも可
動作確認
Vuetifyの確認をしてみます。
都度ビルドが必要であるため、以下を実行しておきます。
laradock@9760cc7de2d2:/var/www/example-app02$ npm run watch
簡易確認
Welcome.vueにVuetifyのボタンを設置して表示を確認します。
Login
リンクを生成する<inter-link>
タグの前くらいに、<v-btn>
を追加します。
<v-btn class="mx-3">サンプル</v-btn>
ブラウザで確認し、テキストではなく、Vuetifyのボタンコンポーネントが表示されれば、ひとまずVuetifyの導入に成功していることでしょう。
http://localhost/app02/
- 本来は、
<v-app>
や<v-main>
で括る必要がありますが、簡易確認ということで割愛しています。
Vuetifyらしいコンポーネントの確認
もう少し高機能なコンポーネントの確認として、Data tables
を設置してみます。
1. コントローラー作成
laradock@9760cc7de2d2:/var/www/example-app02$ php artisan make:controller AnyController
Controller created successfully.
<?php
namespace App\Http\Controllers;
use Inertia\Inertia;
use Illuminate\Http\Request;
class AnyController extends Controller
{
public function responseDataTables(Request $request)
{
$desserts = [
['name' => 'Frozen Yogurt', 'calories' => 159, 'fat' => 6.0, 'carbs' => 24, 'protein' => 4.0, 'iron' => '1%',],
['name' => 'Ice cream sandwich', 'calories' => 237, 'fat' => 9.0, 'carbs' => 37, 'protein' => 4.3, 'iron' => '1%',],
['name' => 'Eclair', 'calories' => 262, 'fat' => 16.0, 'carbs' => 23, 'protein' => 6.0, 'iron' => '7%',],
['name' => 'Cupcake', 'calories' => 305, 'fat' => 3.7, 'carbs' => 67, 'protein' => 4.3, 'iron' => '8%',],
['name' => 'Gingerbread', 'calories' => 356, 'fat' => 16.0, 'carbs' => 49, 'protein' => 3.9, 'iron' => '16%',],
['name' => 'Jelly bean', 'calories' => 375, 'fat' => 0.0, 'carbs' => 94, 'protein' => 0.0, 'iron' => '0%',],
['name' => 'Lollipop', 'calories' => 392, 'fat' => 0.2, 'carbs' => 98, 'protein' => 0, 'iron' => '2%',],
['name' => 'Honeycomb', 'calories' => 408, 'fat' => 3.2, 'carbs' => 87, 'protein' => 6.5, 'iron' => '45%',],
['name' => 'Donut', 'calories' => 452, 'fat' => 25.0, 'carbs' => 51, 'protein' => 4.9, 'iron' => '22%',],
['name' => 'KitKat', 'calories' => 518, 'fat' => 26.0, 'carbs' => 65, 'protein' => 7, 'iron' => '6%',],
];
return Inertia::render('DataTables', [
'desserts' => $desserts,
]);
}
}
2. vueファイル作成
以下のファイルを作成
<template>
<v-app>
<v-main>
<v-card flat tile>
<v-app-bar flat outlined>
<v-toolbar-title>サンプルテーブル</v-toolbar-title>
</v-app-bar>
<v-data-table
:headers="headers"
:items="desserts"
:items-per-page="5"
class="elevation-1"
></v-data-table>
</v-card>
</v-main>
</v-app>
</template>
<script>
export default {
name: 'DataTables',
props: ['desserts'],
data () {
return {
headers: [
{
text: 'Dessert (100g serving)',
align: 'start',
sortable: false,
value: 'name',
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Iron (%)', value: 'iron' },
],
}
},
}
</script>
<style scoped>
</style>
3. ルートファイル調整
web.phpに以下のルートを追加
Route::get('/data-tables', [\App\Http\Controllers\AnyController::class, 'responseDataTables'])->name('data-tables');
4. Welcome.vueに追加したボタンにルートを追加
Welcome.vueの<v-btn>
にhref
を追加
<v-btn class="mx-3" :href="route('data-tables')">サンプル</v-btn>
確認
Welcomeページの「サンプルボタン」をクリックし、データテーブル画面を確認します。
Vuetifyの導入に何らかの不備があると、スタイルが適用されていなかったり、ボタンクリックなどでJavaScriptエラーが発生したりします(ブラウザの開発ツールでコンソールでJavaScriptエラーが出ていないことなども確認が必要です)。
おわりに
Laravel 8の検証がてら Laravel JetStream + Inertia にVuetifyを導入して使ってみました。
Inertia + Vueではデータの受け渡しをprops経由で行うので、データの受け取り後はSPA開発と同等の考え方を適用できそうです。下位方向へのコンポーネント分割もできますし、むしろすべてのvueファイルを分割されたコンポーネントであると捉えれば、SPAでもInertiaでも区別なくコンポーネント開発ができる気がするとかしないとか。(vuex
禁止にすればより近くなりそう)
Vuetifyの場合、コンポーネントを<v-app>
と<v-main>
で囲む必要があるので、Inertiaで構成する場合、すべてのvueファイルにこのタグを記述することになります。
このあたりは、vue-routerの<router-view/>
のようなApp.vueを親にして、子コンポーネントをルーティングする仕組みを自前で作ることですっきりしそうな気がするとかしないとか。(vuex
禁止にすれば(以下略))
今のところ少し触ったくらいなので、引き続き検証を行いつつ、いろいろ見極めたいと思っています。
こんなところで。
Discussion