Memo

メモ > 技術 > フレームワーク: Laravel10 > Reactを使う(登録の実例)

Reactを使う(登録の実例)
以下で紹介されている「簡易 Todo アプリ」を参考に作成する。 Inertia.js を用いた Laravel React SPA 開発のメリットデメリット。 - ガオラボ https://tech.gaogao.asia/react-inertia-laravel/ ■テーブルの作成
$ sail artisan make:migration create_tasks_table
作成されたファイルを、以下のとおり修正する。
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->string('name')->comment('タスク名'); $table->timestamps(); $table->comment('タスク'); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('tasks'); } };
マイグレーションを実行。
$ sail artisan migrate
■モデルの作成
$ sail artisan make:model Task
作成されたファイルを、以下のとおり修正する。
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Task extends Model { protected $fillable = [ 'name', ]; }
■コントローラーの作成
$ sail artisan make:controller TaskController
作成されたファイルを、以下のとおり修正する。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use App\Models\Task; use Illuminate\Support\Facades\Redirect; use Inertia\Inertia; use Inertia\Response as InertiaResponse; class TaskController extends Controller { public function index(): InertiaResponse { $tasks = Task::all(); return Inertia::render('Task/Index', [ 'tasks' => $tasks, ]); } public function store(Request $request): RedirectResponse { Task::create($request->validate([ 'name' => ['required', 'max:20'], ])); return Redirect::route('task.index'); } }
■ビューの作成 以下のファイルを作成する。 resources/js/Pages/Task/Index.jsx を作成。
import GuestLayout from '@/Layouts/GuestLayout'; import { Head, useForm } from "@inertiajs/react"; export default function TaskPage({ tasks }) { const { data, setData, post, errors } = useForm({ name: '' }); const submit = e => { e.preventDefault(); post('/task', { onSuccess: () => setData('name', '') }); }; return ( <GuestLayout> <Head title="Task" /> <div> <h2 className="font-semibold text-xl text-gray-800 leading-tight pb-4">Tasks</h2> <form onSubmit={submit} className="mb-4"> <div> <label className="me-2">タスク</label> <input value={data.name} onChange={ e => setData('name', e.target.value) } className="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm mt-1 me-2" /> <button type="submit" className="items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150"> 登録 </button> {errors.name && <div className="mt-2 text-red-600">{errors.name}</div>} </div> </form> <ul> {tasks.map((task, index) => ( <li key={index} className="mb-2"> {task.id}: {task.name} </li> ))} </ul> </div> </GuestLayout> ); }
■ルーティングの設定 以下のとおり設定する。 routes/web.php
use App\Http\Controllers\TaskController; Route::get('/task', [TaskController::class, 'index'])->name('task.index'); Route::post('/task', [TaskController::class, 'store']);
■動作確認 http://laravel.local/task ■補足1(TypeScriptを使用している場合) TypeScriptを使用している場合、型の指定を追加する必要がある。 一例だが、以下のようにして対応できる。 resources/js/Models/Task.ts を作成。
export default interface Task { id: number; name: string; }
resources/js/Pages/Task/Index.jsx を編集。
import GuestLayout from '@/Layouts/GuestLayout'; import { Head, useForm } from "@inertiajs/react"; import Task from '@/Models/Task'; // 型の指定を読み込む export default function TaskPage({ tasks }: { tasks: Array<Task> }) { // 型の指定 const { data, setData, post, errors } = useForm({ name: '' }); const submit = (e: React.FormEvent<HTMLFormElement>) => { // 型の指定 e.preventDefault(); post('/task', { onSuccess: () => setData('name', '') }); }; return ( <GuestLayout> 〜略〜 </GuestLayout> ); }
ファイルの置き場所として resources/js/Models が適切かどうかは検討したい。 あくまで型定義しか書かないのなら、resources/js/Interfaces の方が適切か。 (resources/js/Types もいいかと思ったが、resources/js/types がすでに存在し、Userなどの型定義が置かれている。 ここはフレームワークデフォルトの型置き場として触らない方がいいか。 その場合でも、Userは専用ファイルに移動させるべきか。) なお、モデルの配列指定は、以下のように書くこともできる。
export default function TaskPage({ tasks }: { tasks: Array<Task> }) { ↓ export default function TaskPage({ tasks }: { tasks: Task[] }) {
なお、エラーを回避するだけの暫定対応なら、以下のとおり「any」を指定することもできる。
export default function TaskPage({ tasks }: { tasks: Array<any> }) { // 型の指定 const { data, setData, post, errors } = useForm({ name: '' }); const submit = (e: any) => { // 型の指定
■補足2 型定義を
import Task from '@/Models/Task';
として読み込む際、
export default function Task({ tasks }: { tasks: Array<Task> }) {
が存在していると名前の重複によりエラーになる。 この場合、
export default function TaskIndex({ tasks }: { tasks: Array<Task> }) {
のようにページの関数名を変更するか。 もしくは
import { default as TaskModel } from '@/Models/Task';
としてモデルを別名で読み込むことで対処できる。 ■補足3 「const { data, setData, post, errors } = useForm(〜略〜);」という書き方はReactならではなものでは無く、JavaScriptの文法に沿ったもの。 オブジェクトの分割代入と呼ばれるもので、以下のように値を受け取ることができる。
function sample() { return { name: 'John Doe', age: 24, occupation: 'Developer', isAdult: function() { if (age >= 20) { return true; } else { return false; } } }; } // オブジェクトの分割代入を使ってプロパティを取り出す const { name, age, isAdult } = sample(); console.log(name); // John Doe console.log(age); // 24 console.log(isAdult()); // true
■補足4 CSRF対策のためのコードを記載していないが、何もしなくても自動的に対策が行われている。 具体的には、トークンが X-CSRF-TOKEN ヘッダに組み込まれ、これにより認証が行われている。

Advertisement