次はコメントの投稿機能を実装します.以前に開発した(認証機能のない)コメント掲示板との大きな違いは投稿時に投稿者(ログイン中のユーザ)の user_id を格納することです.
まず,投稿画面と投稿処理(データベースへの保存処理)のための2つのルートを定義します.もちろん投稿処理は POST メソッドを利用することに注意してください.
routes/web.php(抜粋)
Route::middleware('auth', 'verified')->group(function () {
Route::get('/comments', [CommentController::class, 'index'])->name('comments.index');
Route::get('/comments/create', [CommentController::class, 'create'])->name('comments.create');
Route::get('/comments/{comment_id}', [CommentController::class, 'show'])->name('comments.show');
Route::post('/comments', [CommentController::class, 'store'])->name('comments.store');
});
コントローラに create
と store
関数を定義します.33行目のようにログイン中のユーザを取得することができるので,そのユーザ情報を35行目でビューに渡します.また,store
関数はこれから作成するフォームから送信されたリクエストデータをそのまま表示するだけのコードにしておきます.
app/Http/Controllers/CommentController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Comment;
class CommentController extends Controller
{
public function index()
{
$comments = Comment::orderBy('updated_at','DESC')
->paginate(3);
// dd($comments);
return view('comments.index')
->with('comments', $comments);
}
public function show($comment_id)
{
$comment = Comment::where('id', '=', $comment_id)
->first();
if (!$comment) {
return redirect('/comments');
}
return view('comments.show')
->with('comment', $comment);
}
public function create()
{
$user = Auth::user();
return view('comments.create')
->with('user', $user);
}
public function store(Request $request)
{
dd($request);
}
}
投稿ページのビューを作成します.この16行目には,「コメントの投稿」ボタンを押した時にはルート定義 (routes/web.php) の name
が comments.store
である /comments に,POST メソッドでデータが送信されることを指定しています.また,POST メソッド利用時には17行目のように CSRF トークンを仕込むことがセキュリティを確保する意味でも必須です.
resources/views/comments/create.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Create Comment') }}
</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="my-2 border border-gray-300 rounded-md p-3">
<div class="text-sm text-gray-700">投稿者:</div>
<div class="text-base text-gray-800 indent-4">{{ $user->name }}</div>
<form method="POST" action="{{ route('comments.store') }}">
@csrf
<div class="mt-2">
<x-input-label for="title" :value="__('タイトル')" />
<x-text-input id="title" class="block mt-1 w-full" type="text" name="title" value="{{ old('title') }}" placeholder="コメントのタイトルを入力してください" required autofocus />
<x-input-error :messages="$errors->get('title')" class="mt-2" />
</div>
<div class="mt-2">
<x-input-label for="body" :value="__('本文')" />
<textarea id="body"
class="block mt-1 mb-2 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
type="text" name="body" rows="3" maxlength="250" placeholder="コメントの本文を入力してください">{{ old('body') }}</textarea>
<x-input-error :messages="$errors->get('body')" class="mt-2" />
</div>
<x-primary-button class="block w-full mt-2">
{{ __('コメントの投稿') }}
</x-primary-button>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
一覧ページにコメント投稿ページへのリンクを作成します.慣れないうちは面倒ですが,Tailwind CSS のスタイルも確実に指定をしておきます.
resources/views/comments/index.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Comments') }}
</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="text-base">
<a href="{{ route('comments.create') }}" class="text-blue-500 hover:text-blue-800 hover:underline active:text-blue-900 active:bg-gray-200 transition ease-in-out duration-150">
コメントの投稿
</a>
</div>
@foreach ($comments as $comment)
<div class="my-2 border border-gray-300 rounded-md p-3">
<div class="text-sm text-gray-700">{{ $comment->user->name }}</div>
<div class="font-semibold">
<a href="{{ route('comments.show', $comment->id) }}" class="text-blue-500 hover:text-blue-800 hover:underline active:text-blue-900 active:bg-gray-200 transition ease-in-out duration-150">
{{ $comment->title }}
</a>
</div>
<div class="text-sm text-gray-700">{{ $comment->body }}</div>
</div>
@endforeach
{{ $comments->links() }}
</div>
</div>
</div>
</div>
</x-app-layout>
コメント一覧ページを表示し,「コメントの投稿」リンクをクリックします.
投稿ページのフォームにはプレースホルダーとして「コメントのタイトルを入力して下さい」というような説明があらかじめ表示されています.
タイトルと本文に文字列を入力して「コメントの投稿」ボタンをクリックします.
フォームから送信された内容を確認することができました.
コントローラの store
関数に投稿内容をデータベースに保存する処理を記載します.保存する前にフォームに入力された内容を検証するコードも追加しています.手動でのテストを行いやすいように4行目でタイトルは最大10文字に設定しています.また,7行目でログイン中のユーザ情報を取得して,11行目でその ID を user_id
にセットしています.
app/Http/Controllers/CommentController.php(抜粋)
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required|max:10',
'body' => 'required|max:250'
]);
$user = Auth::user();
$comment = new Comment();
$comment->title = $request->title;
$comment->body = $request->body;
$comment->user_id = $user->id; // 投稿者のIDをセット
$comment->save();
return redirect('/comments');
}
実際に投稿処理を行った結果,一覧表示が次のようになりました.
詳細ページも確認します.
さらに SQLite を操作して,データベースの内容を確認します.ログイン中のユーザの ID も正しく登録されているはずです.
sqlite> SELECT * FROM comments ORDER BY updated_at DESC LIMIT 5; ⏎ id|title|body|user_id|created_at|updated_at 101|タイトルです|コメントの本文を入力します|1|2023-11-05 09:40:34|2023-11-05 09:40:34 39|中島 修平|2245959 京都府若松市西区斉藤町井高2-8-7 ハイツ渚106号 / yamaguchi.minoru@example.com|3|2023-08-30 03:05:17|2023-09-30 22:27:52 64|中津川 七夏|2629310 愛知県加納市北区大垣町近藤7-1-10 / mtanabe@example.net|1|2023-08-30 06:00:30|2023-09-30 19:44:06 4|青山 和也|6606211 愛知県廣川市西区渚町桐山7-10-1 コーポ笹田110号 / nakajima.kazuya@example.net|1|2023-08-30 00:00:00|2023-09-30 17:40:27 99|加藤 翔太|3107803 山形県青山市南区村山町西之園2-2-9 ハイツ井高109号 / ryohei.ogaki@example.org|3|2023-08-30 09:05:47|2023-09-30 06:28:02 sqlite>