プロジェクト作成
プロジェクト一覧画面にあった、追加ボタン
からプロジェクトを追加できるようにしていきましょう。
ルーティング
まずはルーティングから行っていきます。
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('project/create', [ProjectController::class, 'create'])->name('projects.create'); // ここを追加
// プロジェクト作成処理
Route::post('project/store', [ProjectController::class, 'store'])->name('projects.store'); // ここを追加
// タスク一覧画面
Route::get('projects/{id}/tasks', [TaskController::class, 'index'])->name('tasks.index');
});
require __DIR__.'/auth.php';
2つのルートを追加しています。
1つ目がプロジェクト作成画面を表示させるためのcreateメソッド
に処理を渡すルートで、2つ目はプロジェクト作成処理をさせるstoreメソッド
に処理を渡すルートです。
プロジェクト作成画面表示
それではプロジェクト作成画面を表示させる処理やビューを作成していきましょう。
コントローラー編集
まずはルートで定義したプロジェクト作成画面を表示させるcreateメソッド
を作成していきましょう。
task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面
*/
public function index()
{
// ログインユーザーが作成した全てのプロジェクトを取得
$projects = Auth::user()->projects->all();
return view('projects.index', compact('projects'));
}
/**
* プロジェクト作成画面
*/
public function create()
{
return view('projects.create');
}
}
createメソッド
が実行されるとprojects/create.blade.php
が表示されるように処理を記述しました。
ビュー作成&編集
http://localhost/project/create
にアクセスがあった場合、Projectコントローラー
でprojects/create.blade.php
を表示させるように処理を記述したので、create.blade.php
を作成していきましょう。
task-app/resources/views/projects
ディレクトリ配下にcreate.blade.php
を作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/projects/create.blade.php
作成したtask-app/resources/views/projects/create.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('projects.store') }}">
@csrf
<div class="form-group d-flex flex-column flex-md-row">
<label for="project_name" class="col-md-4 col-form-label text-md-right">プロジェクト名:</label>
<div class="col-md-6">
<input id="project_name" type="type" class="form-control @error('project_name') is-invalid @enderror" name="project_name" value="{{ old('project_name') }}" required autocomplete="project_name" autofocus>
@error('project_name')
<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">
<a href="{{ route('projects.index') }}" class="mr-3 btn btn-secondary">戻る</a>
<button type="submit" class="btn btn-primary">作成</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
プロジェクト作成時に必要な情報は作成するプロジェクト名だけなので、必要なinput要素
は1つだけです。
また、formのaction属性
にroute('projects.store')
を指定しているので、フォームが送信されるとプロジェクト作成処理に処理を渡すことができます。
また、後ほどバリデーションなどを実装するので、バリデーションメッセージが出力できるように記述しています。
プロジェクト一覧画面のリンクを変更
プロジェクト一覧画面の追加ボタン
からプロジェクト作成画面に遷移できるように、リンクを変更しておきましょう。
task-app/resources/views/projects/index.blade.php
を下記の通り編集してください。
@extends('layouts.layout')
@section('title')
プロジェクト一覧
@endsection
@section('content')
<div class="container mt-4">
<div class="row">
<div class="col col-md-6 offset-md-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('projects.create') }}" class="btn btn-primary">追加</a>
</div>
<table class="table table-hover mb-0">
<tbody class="text-center">
@foreach ($projects as $project)
<tr>
<td><a href="{{ route('tasks.index', $project->id) }}">{{ $project->project_name }}</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
変更した点は追加ボタン
のhref属性
です。
href属性に{{ route('projects.create') }}
と指定することで、プロジェクト作成画面に遷移することができるようになりました。
プロジェクト作成処理
プロジェクト作成画面を表示することができるようになったので、次はプロジェクト作成処理を記述していきましょう。
コントローラー編集
task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Project; // ここを追加
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面
*/
public function index()
{
// ログインユーザーが作成した全てのプロジェクトを取得
$projects = Auth::user()->projects->all();
return view('projects.index', compact('projects'));
}
/**
* プロジェクト作成画面
*/
public function create()
{
return view('projects.create');
}
/**
* プロジェクト作成処理
*/
public function store(Request $request)
{
// プロジェクト作成処理
$project = Project::create([
'project_name' => $request->project_name,
'user_id' => Auth::id(),
]);
return redirect()->route('projects.index');
}
}
フォームで入力された値は$request
という引数で受け取っています。
また、データを作成する時のメソッドはcreateメソッド
を使用しています。
project_name
にはフォームで入力された値を入れています。
一方user_id
には現在ログインしているユーザーのIDを入れています。
Auth::id()
と記述することでユーザーのIDを取得することができるので覚えておきましょう。
処理が完了したら、プロジェクト一覧画面へリダイレクトしています。
これでプロジェクト作成ができるようになったので、各自動作確認をしておきましょう!
バリデーション
次に、プロジェクト作成時のバリデーションを実装していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:request StoreProjectRequest
作成したtask-app/app/Http/Requests/StoreProjectRequest.php
を下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreProjectRequest 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()
{
return [
'project_name' => 'required|max:30|string',
];
}
public function attributes()
{
return [
'project_name' => 'プロジェクト名',
];
}
}
project_name
は必須であり、最大文字数を30文字で文字列が保存されるようにバリデーションされています。
後は作成したStoreProjectRequestクラス
をProjectコントローラー
で読み込むように処理を追加しましょう。
task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Project;
use App\Http\Requests\StoreProjectRequest; // ここを追加
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面
*/
public function index()
{
// ログインユーザーが作成した全てのプロジェクトを取得
$projects = Auth::user()->projects->all();
return view('projects.index', compact('projects'));
}
/**
* プロジェクト作成画面
*/
public function create()
{
return view('projects.create');
}
/**
* プロジェクト作成処理
*/
public function store(StoreProjectRequest $request)
{
// プロジェクト作成処理
$project = Project::create([
'project_name' => $request->project_name,
'user_id' => Auth::id(),
]);
return redirect()->route('projects.index');
}
}
作成したフォームリクエストをProjectコントローラー
で使用できるようにuse文
を使用しています。
また、storeメソッド
の引数をRequest $request
からStoreProjectRequest $request
に変更しています。
作成したフォームリクエストを適用させるのはこれだけの変更でOKです。
さっそくhttp://localhost/project/createにアクセスして、31文字以上のテキストを入力して作成ボタン
をクリックしましょう。
下記画像のようにバリデーションメッセージが表示されればOKです。
エラー処理
データベースの操作をしているのでエラー処理をしておきましょう。
実装する内容は下記の通りです。
- try-catch文
- トランザクション
- ログ出力
- エラー画面への遷移
task-app/app/Http/Controllers/ProjectController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Project;
use App\Http\Requests\StoreProjectRequest;
use Illuminate\Support\Facades\DB; // ここを追加
use Illuminate\Support\Facades\Log; // ここを追加
class ProjectController extends Controller
{
/**
* プロジェクト一覧画面
*/
public function index()
{
// ログインユーザーが作成した全てのプロジェクトを取得
$projects = Auth::user()->projects->all();
return view('projects.index', compact('projects'));
}
/**
* プロジェクト作成画面
*/
public function create()
{
return view('projects.create');
}
/**
* プロジェクト作成処理
*/
public function store(StoreProjectRequest $request)
{
// トランザクション開始
DB::beginTransaction();
try {
// プロジェクト作成処理
$project = Project::create([
'project_name' => $request->project_name,
'user_id' => Auth::id(),
]);
// トランザクションコミット
DB::commit();
} catch(\Exception $e) {
// トランザクションロールバック
DB::rollBack();
// ログ出力
Log::debug($e);
// エラー画面遷移
abort(500);
}
return redirect()->route('projects.index');
}
}
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'); // ここを追加
});
require __DIR__.'/auth.php';
2つのルートを追加しています。
1つ目がタスク作成画面を表示させるためのcreateメソッド
に処理を渡すルートで、2つ目はタスク作成処理をさせるstoreメソッド
に処理を渡すルートです。
タスク作成画面表示
それではタスク作成画面を表示させる処理やビューを作成していきましょう。
コントローラー編集
まずはルートで設定したcreateメソッド
を作成していきましょう。
task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
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',
));
}
}
createメソッド
が実行されるとprojects/create.blade.php
が表示されるように処理を記述しました。
ビュー作成&編集
http://localhost/projects/1/tasks/create
にアクセスがあった場合、Taskコントローラー
でtasks/create.blade.php
を表示させるように処理を記述したので、create.blade.php
を作成していきましょう。
task-app/resources/views/tasks
ディレクトリ配下にcreate.blade.php
を作成してください。
コマンドで作成する場合は、ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ touch resources/views/tasks/create.blade.php
作成したtask-app/resources/views/tasks/create.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.store', $currentProjectId) }}">
@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') }}" 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="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') }}" 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
タスク作成時に必要な情報はタスク名、期限の2つなので、必要なinput要素
は2つです。
タスクを作成する際にプロジェクトID
が必要になりますが、プロジェクトIDはルートパラメーターとして渡しています。
また、タスク作成時には進捗が必要になりますが、tasksテーブル
の設定(マイグレーション)でtask_status
をデフォルトで0
(未対応)に設定しているので、特にformで設定する必要はありません。
タスク一覧画面のリンクを変更
タスク一覧画面の追加ボタン
からタスク作成画面に遷移できるように、リンクを変更しておきましょう。
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="#">{{ $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.create', $currentProjectId) }}
と指定することで、タスク作成画面に遷移することができるようになりました。
タスク作成処理
タスク作成画面を表示することができるようになったので、次はタスク作成処理を記述していきましょう。
コントローラー編集
task-app/app/Http/Controllers/TaskController.php
を下記の通り編集してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Project;
use App\Models\Task; // ここを追加
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(Request $request, $id)
{
// URLで送られてきたプロジェクトID
$currentProjectId = $id;
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
}
フォームで入力された値は$request
という引数で受け取っています。
また、データを作成する時のメソッドはcreateメソッド
を使用しています。
task_name
とdue_date
にはそれぞれのフォームで入力された値を入れています。
一方project_id
にはルートパラメーターで渡されてきた数値を入れています。
処理が完了したら、タスク一覧画面へリダイレクトしています。
これでタスク作成ができるようになったので、各自動作確認をしておきましょう!
バリデーション
次に、タスク作成時のバリデーションを実装していきます。
ターミナルで下記コマンドをtask-app
ディレクトリ上で実行してください。
$ ./vendor/bin/sail php artisan make:request StoreTaskRequest
作成したtask-app/app/Http/Requests/StoreTaskRequest.php
を下記の通り編集してください。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreTaskRequest 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()
{
return [
'task_name' => 'required|max:100|string',
'due_date' => 'required|date|after_or_equal:today',
];
}
public function attributes()
{
return [
'task_name' => 'タスク名',
'due_date' => '期限',
];
}
public function messages()
{
return [
'due_date.after_or_equal' => ':attributeには今日以降の日付を指定してください。',
];
}
}
task_name
のバリデーションは最大文字数が違うだけで、プロジェクト作成時のproject_name
のバリデーションとほぼ同様です。
due_date
のバリデーションにはdate(日付を表す値であること)
とafter_or_equal(特定の日付と同じまたはそれ以降の日付であること)
を使用しています。
というのも、タスクの期限日が過去の日付だとおかしいので、after_or_equal
の引数としてtoday
を指定することで、今日を含んだ未来の日だけを許容することができます。
また、messagesメソッド
というものも実装しています。
このメソッドはFormRequestクラス
単位でエラーメッセージを定義することができるメソッドです。
'due_date.after_or_equal'
とすることで、due_date
のafter_or_equal
のバリデーションに引っかかった時のみ:attributeには今日以降の日付を指定してください。
というバリデーションメッセージを出力するようになります。
:attribute
と記述すると、attributesメソッド
で記述した期限が:attribute
のところに代入され、最終的なバリデーションメッセージは期限には今日以降の日付を指定してください
となります。
後は作成したStoreTaskRequestクラス
を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; // ここを追加
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;
// タスク作成処理
$task = Task::create([
'project_id' => $currentProjectId,
'task_name' => $request->task_name,
'due_date' => $request->due_date,
]);
return redirect()->route('tasks.index', [
'id' => $currentProjectId,
]);
}
}
作成したフォームリクエストをTaskコントローラー
で使用できるようにuse文
を使用しています。
また、storeメソッド
の引数をRequest $request
からStoreTaskRequest $request
に変更しています。
さっそくhttp://localhost/projects/1/tasks/createにアクセスして、101文字以上のテキストをタスク名に入力し、期限には今日以前の日付を入力して作成ボタン
をクリックしましょう。
下記画像のようにバリデーションメッセージが表示されればOKです。
エラー処理
最後にエラー処理だけしておきましょう。
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,
]);
}
}
これまでのエラー処理で実装している内容と全く同じです。
これでタスク作成の実装は完了です。
タスク編集
次は、タスク一覧画面で各タスク名をクリックされた時に、タスクを編集できるようにしていきましょう。
ルーティング
まずはルーティングから行っていきます。
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
<?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,
]);
}
}
これまでのエラー処理で実装している内容と全く同じです。
これでタスク編集の実装は完了です。