やりたいこと
- 管理者を作って、管理画面は別レイアウトのダッシュボードにて作業できること。
- Reactで開発できる
- CoreUIを使ってデザインできる
ライブラリの選定
管理画面テンプレート/UI
MoonShineはReact使うのにエントリポイントを作りにくかったり、レイアウトテンプレートがあるわけじゃなくて(Overrideすればいいんだけど)Classファイルをがしがし生成して記述してっていうパッケージ方式だったので、やりにくいかも…と思ってLaravelとは疎結合なCoreUIを使ってみる。
ChatGPTに聞いてみたところいくつか候補が挙がって、最後に3つを比較したらこんな感じでChatGPTが回答した。
パッケージ名 | メリット | デメリット | 向き | 不向き |
---|---|---|---|---|
AdminLTE | 豊富なコンポーネント、サードパーティプラグインの対応 | デザインがやや古い、設定が複雑になることがある | 多機能なダッシュボード、レガシーシステム | 最新のデザインやモダンな技術を使う場合 |
CoreUI | モダンなデザイン、React/Vue/Angular対応、拡張性が高い | フレームワーク依存、学習コスト | モダンフロントエンド、SPA | HTML/CSS/JSベースのみのプロジェクト |
Tabler | シンプルで軽量、モダンなUI、カスタマイズ容易 | 機能が少ない、プラグイン対応が少ない | 小規模プロジェクト、シンプルな管理画面 | 大規模プロジェクト、多機能ダッシュボード |
どのパッケージを選ぶかは、プロジェクトの規模や技術的な要件によります。簡単な管理画面が必要な場合はTabler、ReactやVueを使用したSPA管理画面にはCoreUI、多機能で大規模なプロジェクトにはAdminLTEが適しています。|
ということで、CoreUIにした。
認証・認可まわり
JetStreamにしようかなと思ったけど、なんかLaravel Permission でもいいんじゃないか?そこまで二要素認証とか使わないし、Reactに対応してないしで…ということで Laravel Permissionにした。スターターキットはBreeze
Laravelのログイン後の画面にCoreUIの導入
LaravelでBreezeのインストールまでは昨日の記事と同じまま。
インストール
Viteとの連携なので、CoreUI & Vite · CoreUIを見ながら。
npm i --save @coreui/coreui @popperjs/core npm i --save-dev sass
vite.config.js
CoreUIのJavaScriptを使えるようにする。 importで~coreuiのエイリアスが使えるように設定。
import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import react from '@vitejs/plugin-react'; import { resolve } from 'path' export default defineConfig({ resolve: { alias: { '~coreui': resolve(__dirname, 'node_modules/@coreui/coreui'), } }, plugins: [ laravel({ input: ['resources/js/app.tsx'], refresh: true, }), react(), ], server: { host: true, hmr: { host: 'localhost', }, } });
CoreUIのCSSを読み込む
Reactの大元になるresources/js/app.tsx
で読み込む。resources/css/app.css
を resources/css/app.scss
にファイル名を変更し、app..tsxの1行目のimport '../css/app.css';を import '../css/app.scss';にする。
import '../css/app.scss';
そして、resources/css/app.scss
でcoreuiのcssをインポートする。
@tailwind base; @tailwind components; @tailwind utilities; @import "@coreui/coreui/dist/css/coreui.min.css"
ここまでで、npm run dev
してCoreUIのコンポーネントが使えればOK。
Laravel Permissionの導入
インストール
Breezeのインストールを「React with Inertia」「TypeScript」で行う。
Laravel Permissionは Installation in Laravel | laravel-permission | Spatieから
composer require spatie/laravel-permission php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider ./artisan migrate
でインストール。(てか、Laravel Permissionはこの段階ではどうでもいいね…)
認証後のレイアウト周りを変更する。
認証後にはCoreUIのレイアウトになるようにする。試しに、認証後のdashboardとprofileを /admin/dashboard
や /admin/profile
のrouteにしてログインしてないと入れないようにする。
ルーティング
prefixに/adminを付けて、nameにadmin.をつける。
<?php Route::prefix('/admin')->name('admin.')->group(function () { Route::get('/dashboard', function () { return Inertia::render('Dashboard'); })->middleware(['auth', 'verified'])->name('dashboard'); }); Route::middleware('auth')->prefix('/admin')->name('admin.')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); });
ログイン時のControllerを変更
./artisan route:list
すると、
POST login .......................................................... Auth\AuthenticatedSessionController@store
なので、storeメソッドを先ほどのルーティングの変更に合わせて変える。route('admin.dashboard')のところ。
<?php /** * Handle an incoming authentication request. */ public function store(LoginRequest $request): RedirectResponse { $request->authenticate(); $request->session()->regenerate(); return redirect()->intended(route('admin.dashboard', absolute: false)); }
bladeファイルの変更
Inertia.jsは resources/views/app.blade.php
を起点にするのだけど、管理者用はこれを変えたい場合には、app/Http/Middleware/HandleInertiaRequests.php
を変更する。rootViewメソッドをoverrideして自前のview文字列を返す。
<? public function rootView(Request $request) { if (Auth::user()){ return 'admin'; } return parent::rootView($request); }
で、 resources/views/app.blade.php
をコピーして、 管理者用のrootView resources/views/admin.blade.php
を作成。このbladeでは、resources/js/admin.tsx
をコンパイルするようにしておく。
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title inertia>User::{{ config('app.name', 'Laravel') }}</title> <!-- Fonts --> <link rel="preconnect" href="https://fonts.bunny.net"> <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" /> <!-- Scripts --> @routes @viteReactRefresh @vite(['resources/js/admin.tsx', "resources/js/Pages/Admin/{$page['component']}.tsx"]) @inertiaHead </head> <body class="font-sans antialiased"> Admin Page @inertia </body> </html>
このテンプレートにある $page['component']
というところは、どうもInertia::render()の第一引数で指定されているテンプレート名らしい。
Reactテンプレートの変更
テンプレートのコピー
上記のadmin.blade.phpに合わせて、」ログイン後にホーム http://localhost:8000/ に行ったときに表示する resources/js/Pages/Welcome.tsx
を resources/js/Pages/Admin/Welcome.tsx
にコピーする。この「Welcome」の指定は、routes/web.php
で、InertisjsがWelcomeページを表示するようにされているから。
<?php Route::get('/', function () { return Inertia::render('Welcome', [ 'canLogin' => Route::has('login'), 'canRegister' => Route::has('register'), 'laravelVersion' => Application::VERSION, 'phpVersion' => PHP_VERSION, ]); });
ルーティングに関係するところを変更。
Laravelのルーティングを変えたので、Laravelのroute()関数を使っているところは変更が必要になる。tsxファイルのroute()があるところを適宜変更。このroute()は、React/JavaScriptではなくLaravelのヘルパ関数。
とりあえず、以下のファイルを変更。
resources/js/Layouts/AuthenticatedLayout.tsx
resources/js/Pages/Welcome.tsx
resources/js/Pages/Admin/Welcome.tsx
Inertia.jsのレンダリング
先ほど修正した、Welcome.tsxのroute('admin.dashborad')のリンクをクリックすると、JSONでレスポンスが返ってくる。URLも http://localhost:8000/admin/dashboard になっている。
しかし、http://localhost:18000/admin/dashboard を直接アクセスして開くと、JSONではなくHTMLが返ってくる。この時のテンプレートは、resources/views/admin.blade.php
である。そして、このテンプレートの中の $page['component']
はDashboardになっている。
この2つのレスポンスは、routes/web.php
の
<?php Route::prefix('/admin')->name('admin.')->group(function () { Route::get('/dashboard', function () { return Inertia::render('Dashboard'); })->middleware(['auth', 'verified'])->name('dashboard'); });
の return Inertia::render('Dashboard');
による振り分けで、JavaScriptでfetchされた時はJSON、ブラウザから通常アクセスならHTMLを返すようになっている。
Inertis.jsを使っているときの注意点
Inertia.jsを使うと、Laravel側のルートが全部見えてしまう。だから、「URLがわからないからいいよね」って秘密のコッソリルーティングを作って認証を怠っていたりするとあっさりと突破されてしまう。 普通ならやらないことだけど、何の考えなしに作っていたりするとどでかいセキュリティホールになりうる。
テンプレートを作成する。
次の記事に続く…