こんにちは!
PHPのLaravelやJavaScriptでWeb開発をしているフリラーンスエンジニアのファドと申します!
こちらの記事は応用Laravel教材の第6回目の記事になります。
その他の応用Laravel教材を学習したい方は下記リンクから直接教材へ飛ぶことができます。
-
応用Laravel
【応用Laravel教材①】タスク管理アプリを実装してLaravelをより深く知る!〜環境構築からログイン機能実装編〜
-
応用Laravel
【応用Laravel教材②】Laravelでタスク管理アプリ実装!〜テンプレートの作成からログイン画面作成編〜
-
応用Laravel
【応用Laravel教材③】Laravelでタスク管理アプリ実装!〜プロジェクト一覧機能実装からログイン後のリダイレクト先変更編〜
-
応用Laravel
【応用Laravel教材④】Laravelでタスク管理アプリ実装!〜タスク一覧機能実装編〜
-
応用Laravel
【応用Laravel教材⑤】Laravelでタスク管理アプリ実装!〜プロジェクト追加機能実装からタスク追加機能実装編〜
タスク編集
次は、タスク一覧画面で各タスク名をクリックされた時に、タスクを編集できるようにしていきましょう。
ルーティング
まずはルーティングから行っていきます。task-app/routes/web.php
を下記の通り編集してください。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProjectController;
use App\Http\Controllers\TaskController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
// ログイン必須ルート
Route::middleware('auth')->group(function () {
// プロジェクト一覧画面
Route::get('projects', [ProjectController::class, 'index'])->name('projects.index');
// プロジェクト作成画面
Route::get('projects/create', [ProjectController::class, 'create'])->name('projects.create');
// プロジェクト作成処理
Route::post('projects/store', [ProjectController::class, 'store'])->name('projects.store');
// タスク一覧画面
Route::get('projects/{id}/tasks', [TaskController::class, 'index'])->name('tasks.index');
// タスク作成画面
Route::get('projects/{id}/tasks/create', [TaskController::class, 'create'])->name('tasks.create');
// タスク作成処理
Route::post('projects/{id}/tasks/store', [TaskController::class, 'store'])->name('tasks.store');
// タスク編集画面
Route::get('projects/{id}/tasks/edit/{taskId}', [TaskController::class, 'edit'])->name('tasks.edit'); // ここを追加
// タスク編集処理
Route::post('projects/{id}/tasks/update/{taskId}', [TaskController::class, 'update'])->name('tasks.update'); // ここを追加
});
require __DIR__.'/auth.php';
2つのルートを追加しています。
1つ目がタスク編集画面を表示させるためのeditメソッド
に処理を渡すルートで、2つ目はタスク編集処理をさせるupdateメソッド
に処理を渡すルートです。
タスク編集画面表示
それではタスク編集画面を表示させる処理やビューを作成していきましょう。
コントローラー編集
まずはルートで設定したeditメソッド
を作成していきましょう。task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Task;
use App\Http\Requests\StoreTaskRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class TaskController extends Controller
{
/**
* プロジェクトに紐づくタスク一覧
*/
public function index($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// プロジェクト取得
$project = Project::find($currentProjectId);
// 取得したプロジェクトに紐づくタスクを取得
$tasks = $project->tasks->all();
return view('tasks.index', compact(
'currentProjectId',
'tasks',
));
}
/**
* タスク作成画面
*/
public function create($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
return view('tasks.create', compact(
'currentProjectId',
));
}
/**
* タスク作成処理
*/
public function store(StoreTaskRequest $request, $id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// トランザクション開始
DB::beginTransaction();
try {
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
/**
* タスク編集画面
*/
public function edit($id, $taskId)
{
// タスクを取得
$task = Task::find($taskId);
// 進捗のテキスト(Taskモデルの定数取得)
$taskStatusStrings = Task::TASK_STATUS_STRING;
// 進捗のクラス(Taskモデルの定数取得)
$taskStatusClasses = Task::TASK_STATUS_CLASS;
return view('tasks.edit', compact(
'task',
'taskStatusStrings',
'taskStatusClasses',
));
}
}
今回はルートパラメーターが2つ存在しています。
1つがプロジェクトIDでもう1つがタスクIDです。
それぞれ$id
と$taskId
という引数で受け取っています。
まずはfindメソッド
を使用して編集するタスクを取得しましょう。
次に、進捗がデータベース上で数値として保存されているので、数値に結びつくテキストをTaskモデル
から取得します。
Taskモデルから定数であるTASK_STATUS_STRING
を取得するためにはTask::TASK_STATUS_STRING
と記述します。
また、Bootstrapで使用するクラス名も定数化していたので、そちらもTask::TASK_STATUS_CLASS
とすることで取得することができます。
それぞれを$taskStatusStrings
と$taskStatusClasses
という変数に格納しているので、これらの変数の中身を確認したい場合は、$taskStatusClasses = Task::TASK_STATUS_CLASS;
の後にdd($taskStatusStrings, $taskStatusClasses)
と記述しましょう。
下記画像の値が入っていればOKです!
これら取得した値をtasks/edit.blade.php
に渡しましょう。
ビュー作成&編集
http://localhost/projects/1/tasks/edit/1
にアクセスがあった場合、Taskコントローラー
でtasks/edit.blade.php
を表示させるように処理を記述したので、edit.blade.php
を作成していきましょう。task-app/resources/views/tasks
ディレクトリ配下にedit.blade.php
を作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/tasks/edit.blade.php
作成したtask-app/resources/views/tasks/edit.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
タスク編集
@endsection
@section('content')
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header text-center">タスク編集</div>
<div class="card-body">
<form method="POST" action="{{ route('tasks.update', [$task->project_id, $task->id]) }}">
@csrf
<div class="form-group d-flex flex-column flex-md-row">
<label for="task_name" class="col-md-4 col-form-label text-md-right">タスク名:</label>
<div class="col-md-6">
<input id="task_name" type="type" class="form-control @error('task_name') is-invalid @enderror" name="task_name" value="{{ old('task_name', $task->task_name) }}" required autocomplete="task_name" autofocus>
@error('task_name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="task_status" class="col-md-4 col-form-label text-md-right">進捗:</label>
<div class="col-md-6">
<select name="task_status" id="task_status" class="form-select @error('task_status') is-invalid @enderror">
@foreach ($taskStatusStrings as $key => $taskStatusString)
<option @if ($key == old('task_status', $task->task_status)) selected @endif value="{{ $key }}">{{ $taskStatusString }}</option>
@endforeach
</select>
@error('task_status')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex flex-column flex-md-row mt-3">
<label for="due_date" class="col-md-4 col-form-label text-md-right">期限:</label>
<div class="col-md-6">
<input id="due_date" type="date" class="form-control @error('due_date') is-invalid @enderror" name="due_date" value="{{ old('due_date', $task->due_date) }}" required autocomplete="due_date" autofocus>
@error('due_date')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group d-flex mt-3 mb-0">
<div class="col-md-10 col-12 d-flex justify-content-end">
<button type="submit" class="btn btn-primary">編集</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
今回は編集画面ということでoldメソッド
の第二引数にデフォルト値を指定しておきます。
例えばタスク名のinput要素
のoldメソッド
の第二引数にはtask->task_name
が指定されています。
この場合、画面が表示された直後はTaskコントローラー
で取得したタスク情報が表示されます。
進捗の場合はセレクトボックスになっているので、option要素
に工夫が必要になります。@if ($key == old('task_status', $task->task_status)) selected @endif
と記述していますが、これはforeach
の$key
を使用して、完了
、処理済み
、処理中
、未対応
のどれが選択されているのかを表現することができます。
というのも、変数$taskStatusStrings
には4つの要素があり、$key
には0
から3
の数値が入っています。
またtask_status
にも0
から3
の数値が保存されています。
この数値が$key
とold('task_status', $task->task_status)
が等しい時はselected属性
が付与されます。
例えば、タスクの進捗が1
だった場合は$key
が1
と等しくなるので、処理中
が選択されます。
これで編集画面ができました。
タスク一覧画面のリンクを変更
タスク一覧画面から各タスク名をクリックした時に、タスク編集画面に遷移できるようリンクを変更しておきましょう。
task-app/resources/views/tasks/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
タスク一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="column col-md-8 offset-md-2 mt-md-0 mt-3">
<div class="card">
<div class="card-header bg-dark text-light d-flex justify-content-between align-items-center">
<p class="mb-0 h5">タスク</p>
<a href="{{ route('tasks.create', $currentProjectId) }}" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<thead class="text-light" style="background-color: rgb(106, 106, 106)">
<tr class="text-center">
<th scope="col"style="width: 65%">タスク名</th>
<th scope="col" style="width: 15%">進捗</th>
<th scope="col" style="width: 20%">期限</th>
</tr>
</thead>
<tbody class="text-center">
@foreach ($tasks as $task)
<tr>
<td><a href="{{ route('tasks.edit', [$currentProjectId, $task->id]) }}">{{ $task->task_name }}</a></td>
<td><span class="d-inline badge {{ $task->task_status_class }}">{{ $task->task_status_string }}</span></td>
<td>{{ $task->due_date }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
変更した点は各タスク名のhref属性
です。
href属性に{{ route('tasks.edit', [$currentProjectId, $task->id]) }}
と指定することで、タスク編集画面に遷移することができるようになりました。
また、routeメソッド
の第二引数には配列でプロジェクトIDとタスクIDを指定しています。
複数のパラメーターをメソッドに渡したい場合は配列
を使いましょう。
これで各タスク名をクリックすると、タスク編集画面に遷移できるようになりました。
タスク編集処理
タスク編集画面を表示することができるようになったので、次はタスク編集処理を記述していきましょう。
コントローラー編集
task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Task;
use App\Http\Requests\StoreTaskRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class TaskController extends Controller
{
/**
* プロジェクトに紐づくタスク一覧
*/
public function index($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// プロジェクト取得
$project = Project::find($currentProjectId);
// 取得したプロジェクトに紐づくタスクを取得
$tasks = $project->tasks->all();
return view('tasks.index', compact(
'currentProjectId',
'tasks',
));
}
/**
* タスク作成画面
*/
public function create($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
return view('tasks.create', compact(
'currentProjectId',
));
}
/**
* タスク作成処理
*/
public function store(StoreTaskRequest $request, $id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// トランザクション開始
DB::beginTransaction();
try {
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
/**
* タスク編集画面
*/
public function edit($id, $taskId)
{
// タスクを取得
$task = Task::find($taskId);
// 進捗のテキスト(Taskモデルの定数取得)
$taskStatusStrings = Task::TASK_STATUS_STRING;
// 進捗のクラス(Taskモデルの定数取得)
$taskStatusClasses = Task::TASK_STATUS_CLASS;
return view('tasks.edit', compact(
'task',
'taskStatusStrings',
'taskStatusClasses',
));
}
/**
* タスク編集処理
*/
public function update(Request $request, $id, $taskId)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// タスクを取得
$task = Task::find($taskId);
// タスク編集処理(fill)
$task->fill([
'task_name' => $request->task_name,
'task_status' => $request->task_status,
'due_date' => $request->due_date,
]);
// タスク編集処理(save)
$task->save();
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
}
フォームで入力された値は$request
という引数で受け取っています。
また、データを更新する時のメソッドはfillメソッド + saveメソッド
を使用しています。
task_name
、task_status
、due_date
にはそれぞれのフォームで入力された値を入れています。
更新処理が完了したら、タスク一覧画面へリダイレクトしています。
これでタスク編集ができるようになったので、各自動作確認をしておきましょう!
バリデーション
次に、タスク編集時のバリデーションを実装していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:request UpdateTaskRequest
作成したtask-app/app/Http/Requests/UpdateTaskRequest.php
を下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule; // ここを追加
use App\Models\Task; // ここを追加
class UpdateTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
$myTaskStatusRule = Rule::in(array_keys(Task::TASK_STATUS_STRING));
return [
'task_name' => 'required|max:100|string',
'task_status' => ['required', $myTaskStatusRule],
'due_date' => 'required|date',
];
}
public function attributes()
{
return [
'task_name' => 'タスク名',
'task_status' => '進捗',
'due_date' => '期限',
];
}
public function messages()
{
$statuses = implode('、', array_values(Task::TASK_STATUS_STRING));
return [
'task_status.in' => ':attributeには' . $statuses .'のいずれかを選択してください。',
];
}
}
進捗(task_status)
には、入力値が0~3(完了、処理済み、処理中、未対応)
に含まれているか検証する in
を使用します。
array_keys(Task::TASK_STATUS_STRING)
で配列として0~3
を取得できるので、inメソッド
を使ってルールの文字列を作成しています。
つまり、Rule::in(array_keys(Task::TASK_STATUS_STRING));
と記述することでin(0, 1, 2, 3)
となります。
それが変数$myTaskStatusRule
に代入されています。
結果として出力されるルールは以下のようになります。'task_status' => 'required|in(0, 1, 2, 3)'
後は作成したUpdateTaskRequestクラス
をTaskコントローラー
で読み込むように処理を追加しましょう。task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Task;
use App\Http\Requests\StoreTaskRequest;
use App\Http\Requests\UpdateTaskRequest; // ここを追加
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class TaskController extends Controller
{
/**
* プロジェクトに紐づくタスク一覧
*/
public function index($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// プロジェクト取得
$project = Project::find($currentProjectId);
// 取得したプロジェクトに紐づくタスクを取得
$tasks = $project->tasks->all();
return view('tasks.index', compact(
'currentProjectId',
'tasks',
));
}
/**
* タスク作成画面
*/
public function create($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
return view('tasks.create', compact(
'currentProjectId',
));
}
/**
* タスク作成処理
*/
public function store(StoreTaskRequest $request, $id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// トランザクション開始
DB::beginTransaction();
try {
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
/**
* タスク編集画面
*/
public function edit($id, $taskId)
{
// タスクを取得
$task = Task::find($taskId);
// 進捗のテキスト(Taskモデルの定数取得)
$taskStatusStrings = Task::TASK_STATUS_STRING;
// 進捗のクラス(Taskモデルの定数取得)
$taskStatusClasses = Task::TASK_STATUS_CLASS;
return view('tasks.edit', compact(
'task',
'taskStatusStrings',
'taskStatusClasses',
));
}
/**
* タスク編集処理
*/
public function update(UpdateTaskRequest $request, $id, $taskId)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// タスクを取得
$task = Task::find($taskId);
// タスク編集処理(fill)
$task->fill([
'task_name' => $request->task_name,
'task_status' => $request->task_status,
'due_date' => $request->due_date,
]);
// タスク編集処理(save)
$task->save();
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
}
これで、もしtask_status
に0~3
以外の数字入力されていた場合は、messagesメソッド
で定義している:attributeには$statusesのいずれかを選択してください。
というバリデーションメッセージが出力されます。
ちなみに、変数$statuses
には完了
、処理済み
、処理中
、未対応
という文字列が入っています。
そのため、バリデーションに引っかかった場合は進捗には未対応、処理中、処理済み、完了のいずれかを選択してください。
というバリデーションメッセージが出力されるようになります。
エラー処理
最後にエラー処理だけしておきましょう。task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Task;
use App\Http\Requests\StoreTaskRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class TaskController extends Controller
{
/**
* プロジェクトに紐づくタスク一覧
*/
public function index($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// プロジェクト取得
$project = Project::find($currentProjectId);
// 取得したプロジェクトに紐づくタスクを取得
$tasks = $project->tasks->all();
return view('tasks.index', compact(
'currentProjectId',
'tasks',
));
}
/**
* タスク作成画面
*/
public function create($id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
return view('tasks.create', compact(
'currentProjectId',
));
}
/**
* タスク作成処理
*/
public function store(StoreTaskRequest $request, $id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// トランザクション開始
DB::beginTransaction();
try {
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
/**
* タスク編集画面
*/
public function edit($id, $taskId)
{
// タスクを取得
$task = Task::find($taskId);
// 進捗のテキスト(Taskモデルの定数取得)
$taskStatusStrings = Task::TASK_STATUS_STRING;
// 進捗のクラス(Taskモデルの定数取得)
$taskStatusClasses = Task::TASK_STATUS_CLASS;
return view('tasks.edit', compact(
'task',
'taskStatusStrings',
'taskStatusClasses',
));
}
/**
* タスク編集処理
*/
public function update(UpdateTaskRequest $request, $id, $taskId)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// タスクを取得
$task = Task::find($taskId);
// トランザクション開始
DB::beginTransaction();
try {
// タスク編集処理(fill)
$task->fill([
'task_name' => $request->task_name,
'task_status' => $request->task_status,
'due_date' => $request->due_date,
]);
// タスク編集処理(save)
$task->save();
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
}
これまでのエラー処理で実装している内容と全く同じです。
これでタスク編集の実装は完了です。
おめでとうございます!
Laravelの教材を最後までお読みいただき、ありがとうございました!
次は学習した内容の確認としてLaravelの課題に挑戦してみましょう!
Laravel課題
さあ、これまでの学習の成果を存分に発揮させましょう!
プログラミング学習サポートについて
「独学で挫折した。。。」
「一人でのプログラミング学習がしんどい。。。」
「未経験からエンジニア転職をしたいけど何をしたら良いかわからない。。。」
このような悩みをお持ちの方向けに、本教材作成者のファドがMENTAという学習サイトにてあなたのプログラミング学習とエンジニア転職を徹底サポートいたします!
サポート価格はなんと1日あたりたったの約300円!
教材で分からない箇所のサポートはもちろんのこと、本サイトで公開しているすべての課題の解答も公開しております。
また、MENTAで学習を終わらせていただいた方限定で懇意にしていただいている企業さんを紹介することもあります!
なお、サポート内容の詳細は下記の通りです。
- 目標設定
- マインドセット
- オリジナル教材見放題
- オリジナル課題見放題
- オリジナル課題の解答見放題
- 課題コードレビュー
- 教材への無制限質問
- 課題への無制限質問
- ポートフォリオ作成アドバイス
- 褒めのコーチング
いくつかのプランを用意させていただいておりますので、下記より一度ご覧ください!
コメント