今回もポートフォリオを進めていきます。
今回はデータ編集画面を作成します。
データ登録画面に似ていますが、こちらは既存のデータを更新する機能になります。
そのため、登録画面に比べると若干の追加実装が必要になります。
前回

画面遷移と画面表示の作成
それではいつも通りに画面遷移部分だけ作成していきましょう。
いつもの流れなので詳しい説明は省きます。今回は登録画面+詳細画面のような作りになります。
画面での入力が必要なため、基本的な画面構成や機能は登録画面に近くなります。
しかし、登録画面とは違い「既存のレコード」を用いた処理になるため、今回もルートパラメータを使用して対象のレコードを判定していきます。
ソースはこちら
<?php
省略
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
// 以降のルーティングはこの下に書いていく
Route::get('/student', [StudentController::class, 'index'])->name('student.index'); // 生徒一覧
Route::get('/student/{id}/detail', [StudentController::class, 'detail'])->name('student.detail'); // 生徒詳細
Route::get('/student/create', [StudentController::class, 'create'])->name('student.create'); // 生徒登録
Route::post('/student/store', [StudentController::class, 'store'])->name('student.store'); // 生徒登録処理
Route::get('/student/{id}/edit', [StudentController::class, 'edit'])->name('student.edit'); // 生徒編集
});
省略
<?php
namespace App\Http\Controllers;
use App\Models\Student;
use Illuminate\Http\Request;
class StudentController extends Controller
{
省略
/**
* 編集画面
*/
public function edit(Student $id){
return view('student.edit', [
'student' => $id,
]);
}
}
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
生徒編集
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<div class="w-full">
<form method="post" action="{{route('student.store')}}" class="px-8 pt-6 pb-8 mb-3">
@csrf
{{-- 生徒名 --}}
{{-- 生徒氏名 --}}
<div class="mb-4 flex">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_sei">
生徒氏名(姓)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_sei" name="student_sei" type="text" placeholder="姓">
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_mei">
生徒氏名(名)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_mei" name="student_mei" type="text" placeholder="名">
</div>
</div>
{{-- 生徒受講期間 --}}
<div class="mb-4 flex items-center">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_from_date">
受講開始日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_from_date" name="student_from_date" type="date" placeholder="">
</div>
<div class="mr-2">
<label class="block">
</label>
~
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_to_date">
受講終了日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_to_date" name="student_to_date" type="date" placeholder="">
</div>
</div>
{{-- 生徒受講期間 --}}
<div class="mb-4">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_memo">
生徒メモ
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_memo" name="student_memo" placeholder=""></textarea>
</div>
</div>
{{-- 登録ボタン --}}
<button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none">送信</button>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
続いてリンクを作成していきます。
今回は詳細画面から編集画面に遷移できるようにリンクを設置します。
まずはController処理でURLの設定を作成します。
※元々あった「student_detail_url」の部分を削除しています
public function detail(Student $id){
$student = [
'student_name' => $id->student_sei . $id->student_mei, // 生徒名
'kikan_from' => $id->student_from_date, // 期間(開始日)
'kikan_to' => $id->student_to_date, // 期間(終了日)
'student_memo' => $id->student_memo, // 生徒メモ
'student_edit_url' => route('student.edit', ['id' => $id->id]), // 生徒詳細URL
'student_mendan_url' => '', // 面談URL
];
return view('student.detail', [
'student' => $student,
]);
}
続いて、詳細画面のblade「resources/views/student/detail.blade.php」にリンクを配置します。
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
生徒詳細
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<a class="text-sky-500" href="{{ $student['student_edit_url'] }}">編集</a>
<div class="bg-white border shadow-sm rounded-xl p-4 mb-3">
<h3 class="text-lg font-bold text-gray-800">
{{ $student['student_name'] }}
</h3>
<p class="mt-1 text-xs font-medium text-gray-500">
期間({{ $student['kikan_from'] }}~{{ $student['kikan_to'] }})
</p>
<p class="mt-2 text-gray-800">
{{ $student['student_memo'] }}
</p>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
編集画面が表示されればOK

初期情報表示
続いて登録画面と異なる機能である、初期情報表示の実装を行っていきます。
登録画面は新規で情報を登録するため、入力欄は空欄で問題ありませんが、編集画面となるとそうはいきません。
初期表示として登録済みの情報を表示する必要があります。
基本的には詳細画面と同じように情報を表示するだけですが、input要素に値を設定したいため、input用のvalue属性に値を設定していきます。
Controller処理としてModelインスタンスをbladeに渡しているため、今回はこのModelインスタンスを利用して初期値を設定します。
初期値を設定したform部分はこちら
<form method="post" action="{{route('student.store')}}" class="px-8 pt-6 pb-8 mb-3">
@csrf
{{-- 生徒名 --}}
{{-- 生徒氏名 --}}
<div class="mb-4 flex">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_sei">
生徒氏名(姓)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_sei" name="student_sei" type="text" placeholder="姓"
value="{{$student->student_sei}}"
>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_mei">
生徒氏名(名)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_mei" name="student_mei" type="text" placeholder="名"
value="{{$student->student_mei}}"
>
</div>
</div>
{{-- 生徒受講期間 --}}
<div class="mb-4 flex items-center">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_from_date">
受講開始日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_from_date" name="student_from_date" type="date" placeholder=""
value="{{$student->student_from_date}}"
>
</div>
<div class="mr-2">
<label class="block">
</label>
~
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_to_date">
受講終了日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_to_date" name="student_to_date" type="date" placeholder=""
value="{{$student->student_to_date}}"
>
</div>
</div>
{{-- 生徒メモ --}}
<div class="mb-4">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_memo">
生徒メモ
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_memo" name="student_memo" placeholder="">{{$student->student_memo}}</textarea>
</div>
</div>
{{-- 登録ボタン --}}
<button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none">送信</button>
</form>
基本的にはvalue属性に設定していけばOKです。
textareaタグのみ、value属性ではなく開始タグと終了タグの間に値を設定するため気を付けましょう。
このtextareaタグですが、開始タグと終了タグの間のスペースが無視されません。複数行にまたがる記述をする場合
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_memo" name="student_memo" placeholder="">
{{$student->student_memo}}
</textarea>
このように記述してしまうと、値の表示の際にスペースが入ってしまいます。
複数行にまたがった記述にしたい場合は、
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_memo" name="student_memo" placeholder=""
>{{$student->student_memo}}
</textarea>
このように、タグを閉じる「>」が値の直前にすると良いと思います。
横に長すぎるソースは見にくいですからね。私はよく
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="student_memo" name="student_memo" placeholder=""
>{{$student->student_memo}}</textarea>
このような記述を利用しています。
整理したソース全体はこんな感じ。
これで初期値が表示されます。
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
生徒編集
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<div class="w-full">
<form method="post" action="{{route('student.store')}}" class="px-8 pt-6 pb-8 mb-3">
@csrf
{{-- 生徒名 --}}
{{-- 生徒氏名 --}}
<div class="mb-4 flex">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_sei">
生徒氏名(姓)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="student_sei" name="student_sei" type="text" placeholder="姓"
value="{{$student->student_sei}}"
>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_mei">
生徒氏名(名)
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="student_mei" name="student_mei" type="text" placeholder="名"
value="{{$student->student_mei}}"
>
</div>
</div>
{{-- 生徒受講期間 --}}
<div class="mb-4 flex items-center">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_from_date">
受講開始日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="student_from_date" name="student_from_date" type="date" placeholder=""
value="{{$student->student_from_date}}"
>
</div>
<div class="mr-2">
<label class="block">
</label>
~
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_to_date">
受講終了日
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="student_to_date" name="student_to_date" type="date" placeholder=""
value="{{$student->student_to_date}}"
>
</div>
</div>
{{-- 生徒メモ --}}
<div class="mb-4">
<div class="mr-2">
<label class="block text-gray-700 text-sm font-bold mb-1" for="student_memo">
生徒メモ
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="student_memo" name="student_memo" placeholder=""
>{{$student->student_memo}}</textarea>
</div>
</div>
{{-- 登録ボタン --}}
<button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none">
送信
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
登録処理の実装
続いて更新処理を実装していきます。
今回は既に実装済みのstoreメソッドを利用して実装していきましょう。
ルーティングの修正
まず、ルーティングの定義を変更していきます。
更新時にはレコードを指定するためのidをルートパラメータとして含めたいと思います。しかし、新規登録の際はidを含めることが出来ません。
そのため、オプションパラメータ(設定しなくてもOKなパラメータ)としてルートパラメータを定義していきます。
オプションパラメータはパラメータ名の後ろに「?」を付けることで、オプションパラメータとしての定義が可能です。
修正後のルート定義はこちら
※
順番が変わっているため、生徒情報関連ルート定義をすべて記載しています。
変更したものは「生徒登録処理です」。
// 以降のルーティングはこの下に書いていく
Route::get('/student', [StudentController::class, 'index'])->name('student.index'); // 生徒一覧
Route::get('/student/{id}/detail', [StudentController::class, 'detail'])->name('student.detail'); // 生徒詳細
Route::get('/student/create', [StudentController::class, 'create'])->name('student.create'); // 生徒登録
Route::get('/student/{id}/edit', [StudentController::class, 'edit'])->name('student.edit'); // 生徒編集
Route::post('/student/{id?}/store', [StudentController::class, 'store'])->name('student.store'); // 生徒登録処理
formの送信先の設定
編集画面のformタグのaction属性値を修正して、更新処理を呼び出せるようにします。
修正前
<form method="post" action="{{route('student.store')}}" class="px-8 pt-6 pb-8 mb-3">
修正後
<form method="post" action="{{route('student.store', ['id' => $student->id])}}" class="px-8 pt-6 pb-8 mb-3">
更新処理の実装
続いて更新処理を実装していきます。
登録処理と同じようにstoreメソッドが呼び出されるため、そちらを修正します。
public function store(Request $req, Student $id = null){
// Studentモデルのインスタンスを作成
if(is_null($id)){
$student = new Student();
}else{
$student = $id;
}
// リクエストの値を設定
$student->student_sei = $req->input('student_sei');
$student->student_mei = $req->input('student_mei');
$student->student_from_date = $req->input('student_from_date');
$student->student_to_date = $req->input('student_to_date');
$student->student_memo = $req->input('student_memo');
// 保存
$student->save();
return redirect(route('student.index'));
}
変更点を確認していきましょう。
まず、引数部分に「Student $id = null」を追加しました。詳細画面でお話した、ルートパラメータの使用方法に則った記述方法になります。今回はオプションパラメータのため「設定されない」パターンの考慮が必要になります。そのため、「 = null」とすることで「ルートパラメータが設定されていない場合はnull」と指定をする必要があります。
もちろんnull以外の設定も可能なので、必要に合わせて指定しましょう。
続いてModelインスタンスの作成部分です。
元々は「$student = new Student();」のみの記述でしたがif文が記載されています。
これは登録時は新規インスタンスの使用、更新時にはルートパラメータから取得されるインスタンスの使用といったように、登録処理に使用するインスタンスが変わるため、if文で処理を分岐しました。
これ以降、処理の修正はありません。
しかし、これで登録と更新の処理の実装が完了しています。
SQLでは「Insert」「Update」と登録と更新で使用すべきSQLが異なりますが、Laravelではsaveメソッドだけで両方の動作を実現できます。
これはsaveメソッドを実行するインスタンスの作られ方により挙動が決まるためです。
インスタンス化して作成したインスタンスではInsert、既存レコード取得メソッドにより作成されたインスタンスではUpdateがそれぞれ実行されるため、インスタンスの作成部分だけ気を付ければ、簡単に登録・更新メソッドを実装できます。
動作確認を行ってデータの更新が行えることを確認しましょう。
完成…?
これで編集画面の実装も完了です。
作業終了と言いたいところですが、もう一つやってほしい作業があります。
それは「画面からの生徒登録」です。
今回、登録と更新の両方で使用されるメソッドを作成しました。
これは登録処理から見ると「処理内容が変わった」ことになります。そのため、登録処理も正常に動作することを確認しておきましょう。
このように、複数の機能の共通処理となるようなものを修正した際には、影響のある機能も問題なく動作することを確認しておきましょう。if文を追加した場合は全てのパターンを確認しておいた方が良いですね。

動きません。。。
こういうことが起きるため、影響のある範囲の動作確認が大事になってくるわけですね。
影響範囲が大きい場合は、無理に共通処理にするのではなく、別でメソッドを作成するなどの検討をしましょう。
今回のエラーはURLの設定ですかね。
「//」とおかしなURLになってしまいました。
ルート定義を修正します。
Route::post('/student/store/{id?}', [StudentController::class, 'store'])->name('student.store'); // 生徒登録処理
無事、新規登録も完了しました。
修正を加えてしまったので、更新側の処理も再度確認しておくと良いと思います。
今回はここまで。
編集処理は詳細+登録処理といった感じなので、なんとなく処理が想像できたかもしれません。
プログラミングは処理と処理の組み合わせで、機能を構築していきます。
再利用できるものを作成したり、過去のアルゴリズムを使いまわすことも多々あります。
部品化を意識できるようになるととても良いですね。