Laravel8でTodoアプリを作成するパート③です。
前回までの記事で、タスクの一覧・詳細機能が実装しました。
今回からタスクの追加やバリデーションを解説していきます。
環境構築手順やdockerの基本コマンドなどは、【簡単】Laravel8・php8・mysql8のDocker環境構築で解説しています。
PHPコンテナの入り方
laravel_todo % docker-compose start
または、docker Desktopからコンテナを起動させる。
コンテナが起動したら、laravel_todo % docker psでコンテナの情報を確認する。
laravel_todo % docker exec -it コンテナIDまたはNAMES bash
で、PHPのコンテナに入る。
例)
laravel_todo % docker exec -it laravel_todo_php_1 bash
/var/www#
とかになればOK
【動作環境】
Mac intel
PHP8
Laravel8
apache2.4
Mysql8
phpmyadmin
Composer 2.2.6
node 12系
休日で空いた時間の暇つぶしを探せるアプリを公開しています。
タスクの追加機能を実装する
まずはタスクを追加する画面に遷移できることから始めましょう。
タスク追加画面を新規作成します。
<h1>タスク追加</h1>
ルーティングを追加
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TasksController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', [TasksController::class, 'index'])->name('tasks.index');
// 詳細ページ
Route::get('/{id}', [TasksController::class, 'show'])->name('tasks.show');
// タスク追加
Route::get('/tasks/add', [TasksController::class, 'add'])->name('tasks.add');
タスク追加のパスを/addではなく、/tasks/addにしている理由は、/addだとshowアクションを読んでしまうためです。ここではshowアクションのパスは/{id}となっていますが、{id}の値は数字でも文字列でも記号でもなんでもいいので、/addは/{id}のパスと同じと判断されてしまうことに注意!
コントローラーにタスク追加用のaddアクションを追加します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
class TasksController extends Controller
{
/**
* 一覧画面
*/
public function index()
{
$tasks = Task::get();
return view('tasks.index', compact('tasks'));
}
/**
* 詳細画面
*/
public function show($id)
{
$task = Task::find($id);
return view('tasks.show', compact('task'));
}
/**
* タスク追加
*/
public function add()
{
return view('tasks.add');
}
}
これで、tasks/addにアクセスするとタスク追加画面を呼び出します。
それでは一覧画面からタスク追加画面に遷移できるようにindex.blade.phpを以下のように修正しましょう。
<style>
h1 {
text-align: center;
padding: 30px;
}
.container {
width: 80%;
margin: 0 auto;
}
---------以下を追加------------
.task__add {
text-align: right;
padding-bottom: 10px;
}
---------追加終わり-------------
table {
border-spacing: 0;
border-collapse: collapse;
border-bottom: 1px solid #aaa;
color: #555;
width: 100%;
}
th {
border-top: 1px solid #aaa;
background-color: #f5f5f5;
padding: 10px 0 10px 6px;
text-align: center;
}
td {
border-top: 1px solid #aaa;
padding: 10px 0 10px 6px;
text-align: center;
}
a {
margin-right: 20px;
}
</style>
<h1>タスク一覧</h1>
<div class="container">
--------以下を追加---------------
<div class="task__add">
<a href="{{ route('tasks.add') }}">+タスクを追加する</a>
</div>
--------追加終わり---------------
<table>
<tr>
<th>タスク</th>
<th>アクション</th>
</tr>
@foreach ($tasks as $task)
<tr>
<td>{{ $task->name }}</td>
<td>
<a href="{{ route('tasks.show', ['id' => $task->id]) }}">詳細</a>
<a href="">編集</a>
<a href="">削除</a>
</td>
</tr>
@endforeach
</table>
</div>
これで一覧画面から+タスクを追加するのリンク押したら、タスク追加画面に遷移できました。
タスク追加画面を以下のように修正します。
<style>
h1 {
text-align: center;
padding: 30px;
}
.form {
width: 80%;
margin: 0 auto;
text-align: center;
}
.form-group {
padding-bottom: 50px;
}
span {
color: red;
}
input {
width: 60%;
height: 30px;
}
textarea {
width: 60%;
}
</style>
<h1>タスク追加</h1>
<form action="" method="" class="form">
@csrf
<div class="form-group">
<label for="name">タスク<span>(必須)</span></label><br>
<input type="text" name="name" maxlength="30" placeholder="タスクは30文字で書きましょう。">
</div>
<div class="form-group">
<label for="content">タスク内容<span>(必須)</span></label><br>
<textarea rows="5" name="content" placeholder="タスク内容を具体的に書きましょう"></textarea>
</div>
<button type="submit">追加する</button>
</form>
タスク追加画面(/tasks/add)にフォームが追加され、タスク追加画面っぽくなりました。
次にやるべきことは、タスク追加画面でフォームに入力し追加するボタンを押したときに入力した値をDBにいれることです。
そのためには、コントローラーにタスク追加-DBに値を入れる処理を追加し、ルーティングも追加する必要があるのでそちらを実装していきましょう。
ルーティングを追加します。
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TasksController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', [TasksController::class, 'index'])->name('tasks.index');
// 詳細ページ
Route::get('/{id}', [TasksController::class, 'show'])->name('tasks.show');
// タスク追加
Route::get('/tasks/add', [TasksController::class, 'add'])->name('tasks.add');
// タスク追加-DBに値を入れる処理
Route::post('/tasks/add', [TasksController::class, 'store'])->name('tasks.store');
コントローラーにstoreアクションを追加します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
class TasksController extends Controller
{
/**
* 一覧画面
*/
public function index()
{
$tasks = Task::get();
return view('tasks.index', compact('tasks'));
}
/**
* 詳細画面
*/
public function show($id)
{
$task = Task::find($id);
return view('tasks.show', compact('task'));
}
/**
* タスク追加
*/
public function add()
{
return view('tasks.add');
}
/**
* タスク追加-DBに値を入れる処理
*/
public function store(Request $request)
{
// tasksテーブルにフォームで入力した値を挿入する
$result = Task::create([
'name' => $request->name,
'content' => $request->content,
]);
// タスク一覧画面にリダイレクト
return redirect()->route('tasks.index');
}
}
モデルを修正します。
修正する理由は、モデルのTask.phpとtasksテーブルの関連づけをまだしていなかった、登録や編集できるカラムなどをfillableで指定する必要があるからです。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
// モデルに関連づけるテーブル
protected $table = 'tasks';
// テーブルに関連づける主キー
protected $primaryKey = 'id';
// 登録・編集ができるカラムのリスト
protected $fillable = [
'name',
'content',
'created_at',
'updated_at',
];
}
タスク追加画面(tasks/add add.blade.php)で追加するボタン押したら、Tasksコントローラーのstoreアクションが実行されるようにformのactionとmethodを追記します。
----------修正前------------
<form action="" method="" class="form">
--------修正後--------------
<form action="{{ route('tasks.store') }}" method="POST" class="form">
@csrf
<div class="form-group">
<label for="name">タスク<span>(必須)</span></label><br>
<input type="text" name="name" maxlength="30" placeholder="タスクは30文字で書きましょう。">
</div>
<div class="form-group">
<label for="content">タスク内容<span>(必須)</span></label><br>
<textarea rows="5" name="content" placeholder="タスク内容を具体的に書きましょう"></textarea>
</div>
<button type="submit">追加する</button>
</form>
以上で、追加ボタン押したときにフォームに入力した値がtasksテーブルにデータとして保存されます。
保存処理が終わると、一覧画面にリダイレクトされるので、先ほど登録した値も画面に表示されていることが確認できるでしょう。
また、最新のタスクが一番上に表示されることが好ましいので、TasksControllerのindexアクションを少し修正します。
※具体的には、更新日時が最新順に並び替えて、一覧表示させます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
class TasksController extends Controller
{
/**
* 一覧画面
*/
public function index()
{
----------修正前------------
$tasks = Task::get();
----------修正前------------
----------修正後-------------
// 更新日時が最新順に並び替えて、タスク一覧を取得
$tasks = Task::orderBy('updated_at', 'desc')->get();
----------修正後-------------
return view('tasks.index', compact('tasks'));
}
/**
* 詳細画面
*/
public function show($id)
{
$task = Task::find($id);
return view('tasks.show', compact('task'));
}
/**
* タスク追加
*/
public function add()
{
return view('tasks.add');
}
/**
* タスク追加-DBに値を入れる処理
*/
public function store(Request $request)
{
// tasksテーブルにフォームで入力した値を挿入する
$result = Task::create([
'name' => $request->name,
'content' => $request->content,
]);
// タスク一覧画面にリダイレクト
return redirect()->route('tasks.index');
}
}
descは降順(5,4,3,2,1とか日付なら最新→過去)です。反対にascは昇順(1,2,3,4,5で日付なら過去→最新)となります。
PHPは一番最新なので、タスクのリストも一番上に表示されていることが確認できました。
バリデーションを実装する
次にバリデーションを実装していきます。
Laravelではバリデーションを実装するときに、バリデーションをチェックするためのファイルを作成するのが適切です。(フォームリクエスト)
なので、フォームリクエストを作成していきましょう。
Laravelのバリデーションついては、Laravel8でバリデーション実装を解説を参照してください。
/var/www# php artisan make:request TasksRequest
Request created successfully.
app>Http>Requests以下に新規ファイルが作成されます。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}
まずは最初にauthorizeをtrueにしておきます。※falseのままだとアクションを実行できません。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
------修正前-------
return false;
------修正後-------
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}
rulesにバリデーションしたい内容を書いていきます。
今回チェックしたい内容は以下です。
- タスクは必須
- タスクは30文字以内
- タスクの内容は必須
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TaskRequest 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
*/
public function rules()
{
return [
'name' => 'required|max:30', // 必須、30文字以内
'content' => 'required', // 必須
];
}
}
messagesアクションを追加すれば、バリデーションのエラーメッセージもカスタマイズできます。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TaskRequest 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
*/
public function rules()
{
return [
'name' => 'required|max:30', // 必須、30文字以内
'content' => 'required', // 必須
];
}
/**
* エラーメッセージ
*/
public function messages()
{
return [
'name.required' => 'タスクは必須です',
'name.max' => 'タスクは30文字以内で入力してください',
'content.required' => 'タスク内容は必須です',
];
}
}
フォームリクエストにバリデーションルールが記載できたので、コントローラーで適用させます。
※コントローラー冒頭のuse App\Http\Requests\TaskRequest;を追加忘れずに!
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
use App\Http\Requests\TaskRequest; // 追加するのを忘れない
class TasksController extends Controller
{
/**
* 一覧画面
*/
public function index()
{
// 更新日時が最新順に並び替えて、タスク一覧を取得
$tasks = Task::orderBy('updated_at', 'desc')->get();
return view('tasks.index', compact('tasks'));
}
/**
* 詳細画面
*/
public function show($id)
{
$task = Task::find($id);
return view('tasks.show', compact('task'));
}
/**
* タスク追加
*/
public function add()
{
return view('tasks.add');
}
/**
* タスク追加-DBに値を入れる処理
*/
-------修正前----------
public function store(Request $request)
-------修正後-----------
public function store(TaskRequest $request)
{
// tasksテーブルにフォームで入力した値を挿入する
$result = Task::create([
'name' => $request->name,
'content' => $request->content,
]);
// タスク一覧画面にリダイレクト
return redirect()->route('tasks.index');
}
}
store(TaskRequest $request)と変わりましたが、これはstoreアクションを実行する前にTaskRequestで先ほど書いたバリデーションをチェックして何も問題なければstoreアクションのDB処理が始まるってわけです。
もしバリデーションエラーがあれば、storeアクションは実行されずに、エラーメッセージを返します。
add.blade.phpにエラーメッセージがあった場合に表示させる処理を書きます。
また、old関数を使ってvalue=”{{ old(‘name’) }}と書くことで、エラーメッセージがあったときに入力していた内容を残してくれます。
詳しくはLaravelのビューでold関数を使い、フォームに値を残す方法を参照ください。
<style>
h1 {
text-align: center;
padding: 30px;
}
-------追加-------------
.error {
text-align: center;
}
.error__message {
color: red;
}
-------追加終わり---------
.form {
width: 80%;
margin: 0 auto;
text-align: center;
}
.form-group {
padding-bottom: 50px;
}
span {
color: red;
}
input {
width: 60%;
height: 30px;
}
textarea {
width: 60%;
}
</style>
<h1>タスク追加</h1>
-------追加-------------
<div class="error">
@foreach ($errors->all() as $error)
<p class="error__message">{{$error}}</p>
@endforeach
</div>
-------追加終わり---------
<form action="{{ route('tasks.store') }}" method="POST" class="form">
@csrf
<div class="form-group">
<label for="name">タスク<span>(必須)</span></label><br>
<input type="text" name="name" maxlength="30" placeholder="タスクは30文字で書きましょう。" value="{{ old('name') }}">
</div>
<div class="form-group">
<label for="content">タスク内容<span>(必須)</span></label><br>
<textarea rows="5" name="content" placeholder="タスク内容を具体的に書きましょう">{{ old('content') }}</textarea>
</div>
<button type="submit">追加する</button>
</form>
これでタスクが未入力の場合にエラーメッセージが表示できるようになりました。
今回の記事はここまでです。
次回は編集・削除処理を実装していきます。
疑問点などがあればお気軽にページ下部にあるコメントやお問合せよりご質問ください。
休日で空いた時間の暇つぶしを探せるアプリを公開しています。
コメント