Laravel9でCRUDを実装する記事を書きました。
登録時は以下のような画面遷移にしています。
一覧画面→入力画面→一覧画面(登録完了したデータを表示)
編集時も同様で、以下のような流れです。
一覧画面→編集画面→一覧画面(更新完了したデータを表示)
CRUDを実装するという意味では、上記が必要最低限かなというところですが、実際の業務では、入力内容を確認する画面があったり、処理が完了した旨を表示する画面があったりすることが多いです。
具体的には以下のような流れです。
一覧画面→入力画面→確認画面→完了画面→一覧画面
今回は、上記の流れになるように確認画面と完了画面を追加してみます。
環境
以下の環境で実装しています。
2023年1月に実装した時点の情報になります。
- PHP:8.1.14
- Laravel:9.46.0
新規登録時に確認画面を表示する
まず、新規登録時に確認画面を表示する処理を追加します。
新規登録時に、以下のように入力項目と「入力内容を確認する」ボタンを用意しました。
ルーティングは以下のようにしました。
Route::controller(ShopsController::class)->name('shops.')->group(function() {
Route::post('shops/confirm', 'confirm')->name('confirm');
});
「入力内容を確認する」ボタンをクリックした時に、上記のルーティングへ post します。
<form method="post" action="{{ route('shops.confirm') }}" class="mt-6 space-y-6">
@csrf
<div>
<x-input-label for="name" value="名前" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name')" required autofocus />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
<div>
<x-input-label for="address" value="住所" />
<x-text-input id="address" name="address" type="text" class="mt-1 block w-full" :value="old('address')" required />
<x-input-error class="mt-2" :messages="$errors->get('address')" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>入力内容を確認する</x-primary-button>
</div>
</form>
コントローラーは以下のようにしました。
リクエスト内容をそのまま確認画面のビューへ渡しています。
public function confirm(ShopRequest $request)
{
return view('shops.confirm', [
'inputs' => $request->all(),
]);
}
確認画面は以下のように、入力内容を表示します。
確認画面のビューは以下のようにしました。
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Shops') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<form method="post" action="{{ route('shops.store') }}" class="mt-6 space-y-6">
@csrf
<input type="hidden" name="name" value="{{ $inputs['name'] }}">
<input type="hidden" name="address" value="{{ $inputs['address'] }}">
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
<div class="border-t">
<dl>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium">名前</dt>
<dd class="mt-1 text-sm sm:col-span-2 sm:mt-0">{{ $inputs['name'] }}</dd>
</div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium">住所</dt>
<dd class="mt-1 text-sm sm:col-span-2 sm:mt-0">{{ $inputs['address'] }}</dd>
</div>
</dl>
</div>
</div>
<div class="mt-4">
<x-primary-button name="back">修正する</x-primary-button>
<x-primary-button>この内容で登録する</x-primary-button>
</div>
</form>
</div>
</div>
</x-app-layout>
確認画面から入力画面に戻る
入力内容を修正したい場合に、前の画面(入力画面)に戻るボタンを用意します。
「修正する」ボタンをクリックした場合は、以下のように対象のルーティングへ submit します。
この時、「登録する」ボタンと「修正する」ボタンを判別するために、「name=”back”」を付与しています。
<form method="post" action="{{ route('shops.store') }}" class="mt-6 space-y-6">
(中略)
<x-primary-button name="back">修正する</x-primary-button>
(中略)
</form>
Route::controller(ShopsController::class)->name('shops.')->group(function() {
Route::post('shops/complete', 'store')->name('store');
});
コントローラーは以下のようにしました。
「修正する」ボタンをクリックした場合は、リクエストに「back」という文字列が含まれるので、入力内容を引き継いで、入力画面にリダイレクトしています。
public function store(ShopRequest $request)
{
if ($request->has('back')) {
return Redirect::route('shops.create')->withInput($request->all());
}
(中略)
}
新規登録時に完了画面を表示する
確認画面で「この内容で登録する」ボタンをクリックすることで、登録を行い、完了画面を表示するようにします。
<form method="post" action="{{ route('shops.store') }}" class="mt-6 space-y-6">
(中略)
<x-primary-button>この内容で登録する</x-primary-button>
(中略)
</form>
コントローラーは上記の「修正する」ボタンと同様のものです。
上記の戻る処理の判定後、登録を行い、完了画面を表示します。
public function store(ShopRequest $request)
{
if ($request->has('back')) {
return Redirect::route('shops.create')->withInput($request->all());
}
Shop::create($request->all());
return view('shops.complete')->with('status', 'shops-stored');
}
ビューは以下のようにしました。
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Shops') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
@if ($status == 'shops-stored')
<p class="max-w-2xl text-sm text-gray-500">新規登録が完了しました。</p>
@endif
</div>
</div>
<div class="mt-4">
<a href="{{ route('shops.index') }}" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white tracking-widest hover:bg-gray-700">戻る</a>
</div>
</div>
</div>
</x-app-layout>
編集時に確認画面を表示する
続いて、編集時に確認画面を表示する処理を追加します。
画面のレイアウトは新規登録時と同様です。
編集時のルーティングは、モデルの暗黙の結合を含むため、新規登録時とは分けて、以下のようにしました。
Route::controller(ShopsController::class)->name('shops.')->group(function() {
Route::post('shops/{shop}/edit-confirm', 'updateConfirm')->name('update_confirm');
});
コントローラーは以下のようにしました。
編集時は引数にモデルを含むので、リクエスト内容に加え、モデルも確認画面のビューへ渡しています。
public function updateConfirm(ShopRequest $request, Shop $shop)
{
return view('shops.confirm', [
'shop' => $shop,
'inputs' => $request->all(),
]);
}
確認画面は以下のように、入力内容を表示します。
新規登録時とはボタンの文言が異なっています。
確認画面のビューは以下のようにしました。
新規登録時とファイルは同一で、変更点が3点あります。
1つ目は、確認画面→完了画面遷移時の submit 先を、新規登録 or 編集で分けています。
<form method="post" action="{{ !isset($shop) ? route('shops.store') : route('shops.update', $shop->id) }}" class="mt-6 space-y-6">
Route::controller(ShopsController::class)->name('shops.')->group(function() {
Route::post('shops/complete', 'store')->name('store');
Route::put('shops/{shop}/edit-complete', 'update')->name('update'); // 追加
});
2つ目は、編集時は put で submit するようにしています。
@csrf
@isset($shop) // 追加
@method('PUT') // 追加
@endisset // 追加
<input type="hidden" name="name" value="{{ $inputs['name'] }}">
<input type="hidden" name="address" value="{{ $inputs['address'] }}">
3つ目は、新規登録 or 編集でボタンの文言を変えています。
<x-primary-button>{{ !isset($shop) ? 'この内容で登録する' : 'この内容で更新する' }}</x-primary-button>
編集時に完了画面を表示する
コントローラーは以下のようにしました。
構成は新規登録時と同様です。
public function update(ShopRequest $request, Shop $shop)
{
if ($request->has('back')) {
return Redirect::route('shops.edit', $shop)->withInput($request->all());
}
$shop->update($request->all());
return view('shops.complete')->with('status', 'shops-updated');
}
完了画面は、表示するメッセージが異なります。
@if ($status == 'shops-stored')
<p class="max-w-2xl text-sm text-gray-500">新規登録が完了しました。</p>
@elseif ($status == 'shops-updated') // 追加
<p class="max-w-2xl text-sm text-gray-500">更新が完了しました。</p> // 追加
@endif