第13回 Laravel10 環境構築メモ(Laravel+Typescript+React+inertiaでCreateをグレードアップしてみる)
はじめに
前回、登録画面を作成したのですが、今日は、それを少し改造してみます。
FormRequestを使ってValidateする(ChatGPTに聞いたらおすすめされた)
成功して一覧画面に戻った時にFlashメッセージを表示する
ReactのTypescriptをしっかりと修正する(Interfaceとか定義してなかった&型指定してなかったので、叱られてた)
ちなみに、今更ながらGithub Copilotを使い始めたのですが、若干、生産性が上がった気がします。若干ね。コード予測してtabで補完できるのが、たまに、いい感じですね。
FormRequestを作成
artisanのmakeを使ってFormRequestを作成します。
php artisan make:request Greeting/StoreRequest
出来たファイルを修正します。authorizeのreturnをtrueにしないと画面表示でエラーになるので、お忘れなく。
app/Http/Requests/Greeting/StoreRequest.php
<?php
namespace App\Http\Requests\Greeting;
use Illuminate\Foundation\Http\FormRequest;
class StoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'country' => ['required', 'max:200'],
'message' => ['required', 'max:200'],
];
}
}
Controllerの修正
今回、修正するのはstoreだけです。引数をStoreRequestに変更してください。あと、flashメッセージを表示したいので、withでメッセージも追加します。
app/Http/Controllers/GreetingController.php
/**
* Store a newly created resource in storage.
*/
public function store(StoreRequest $request)
{
Greeting::create($request->validated());
return to_route('greetings.index')->with('flash', 'Greeting created successfully');
}
HandleInertiaRequestsの修正
ここから若干、難しくなるのですが、React側でusePageを使ってshared data(いろんなページで値を取得したいようなものを入れとく時にたぶん使うみたいなんですが、詳しくは、本家のこのページ見てください)を取得する事ができるのですが、今回、flashメッセージをそこに追加してみました。Controllerでsessionに突っ込んでるのを一回取り出して、shared dataに突っ込みなおすという事をしています。
app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
],
'ziggy' => fn () => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
'flash' => fn () => $request->session()->get('flash'),
];
}
resources/js/types/index.d.tsの修正
HandleInertiaRequestsの修正だけでもReact側でデータは取れるんですが、そのままだとESLintで叱られるのでPagePropsを拡張します。PagePropsは、usePageでpropsを取得するときのtypeになります。
↓な感じで既に拡張されてauthがいらっしゃるんですが、さらにflashを追加してみます。
resources/js/types/index.d.ts
export type PageProps<
T extends Record<string, unknown> = Record<string, unknown>
> = T & {
auth: {
user: User
}
flash: string
}
あとは、一覧画面と登録画面をESLintで叱られないようにしっかり修正しつつ、flashメッセージが一覧画面で表示されるようにします。
resources/js/Pages/Greeting/index.tsx
import { Head, usePage } from '@inertiajs/react'
import { type FC } from 'react'
import { type PageProps } from '@/types'
interface Props {
greetings: {
data: Greeting[]
}
title: string
}
interface Greeting {
id: number
country: string
message: string
}
const Index: FC<Props> = ({ greetings, title }) => {
const { flash } = usePage<PageProps>().props
return (
<>
<Head title={title} />
{flash !== null && (
<h1 className="bg-green-400 text-white">{flash}</h1>
)}
{greetings.data.map((greeting: Greeting) => (
<div key={greeting.id}>
<h2>
#{greeting.id} - {greeting.country}
</h2>
<p>{greeting.message}</p>
</div>
))}
</>
)
}
export default Index
resources/js/Pages/Greeting/create.tsx
import { Head, router, usePage } from '@inertiajs/react'
import React, { type FC, useState } from 'react'
import GuestLayout from '@/Layouts/GuestLayout'
import InputLabel from '@/Components/InputLabel'
import TextInput from '@/Components/TextInput'
import PrimaryButton from '@/Components/PrimaryButton'
import InputError from '@/Components/InputError'
const Create: FC = () => {
const { errors } = usePage().props
const [values, setValues] = useState({
country: '',
message: ''
})
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
const key = e.target.id
const value = e.target.value
setValues(values => ({
...values,
[key]: value
}))
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault()
router.post('/greetings', values)
}
return (
<GuestLayout>
<Head title='Greeting - Create' />
<form onSubmit={handleSubmit}>
<div>
<InputLabel htmlFor="country">Country:</InputLabel>
<TextInput id="country" value={values.country} onChange={handleChange} className="mt-1 block w-full"/>
<InputError message={errors.country} className="mt-2" />
</div>
<div>
<InputLabel htmlFor="message">Message:</InputLabel>
<TextInput id="message" value={values.message} onChange={handleChange} className="mt-1 block w-full"/>
<InputError message={errors.message} className="mt-2" />
</div>
<div className="flex items-center justify-end mt-4">
<PrimaryButton className="ms-4" type="submit">
Submit
</PrimaryButton>
</div>
</form>
</GuestLayout>
)
}
export default Create
実際に登録して、flashメッセージが表示されるか確認してみる
登録画面(http://localhost/greetings/create)で適当に値を入力してSUBMITしてみてください。下記の様に画面上部にメッセージが表示されればOKです。
さいごに
これ余談なのですが、artisanのコマンドを実行するときに毎度、Xdebugのこのエラーが出て邪魔でした。
Xdebug: [Step Debug] Time-out connecting to debugging client, waited: 200 ms
どうすると出なくなるかというとphp.iniを下記の通り修正してください。
# xdebug.start_with_request=yes
xdebug.start_with_request = trigger
試してないのですが、Xdebug helperとかをChromeに入れないとdebug動かなくなるかもですが、気になる人は修正してみてください。