Laravel を使って簡単なCRUD機能を実装(フォームの作成編)
1. モデルとマイグレーションファイルの作成
まずデータベース部分すなわちモデルとマイグレーションファイルを作成していく。
次のコードを実行し、マイグレーションファイルを作成する。
sail artisan make:model Post -m
そして以下のコードを書く
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id(); // 自動増分IDを作成
$table->string('title'); // 'title' という名前のカラム(文字列型)
$table->text('body'); // 'body' という名前のカラム(テキスト型)
$table->text('image'); // 'image' という名前のカラム(テキスト型)
$table->timestamps(); // 作成日時と更新日時を記録するカラム(created_at, updated_at)
});
}
以下説明
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
• Migration: マイグレーションの基底クラスです。これを使って、データベースのテーブルを作成したり削除したりします。
• Blueprint: テーブルを構築するためのクラスで、どんなカラム(列)を持つテーブルを作るかを定義します。
• Schema: LaravelのDBスキーマビルダーです。テーブルの作成や変更を行うために使います。
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id(); // 自動増分IDを作成
$table->string('title'); // 'title' という名前のカラム(文字列型)
$table->text('body'); // 'body' という名前のカラム(テキスト型)
$table->text('image'); // 'image' という名前のカラム(テキスト型)
$table->timestamps(); // 作成日時と更新日時を記録するカラム(created_at, updated_at)
});
}
• Schema::create('posts', ...):posts という名前のテーブルを作成します。
• $table->id():自動増分される主キーのIDカラムを追加します。
• $table->string('title'):title という文字列型のカラムを追加します。
• $table->text('body'):body というテキスト型のカラムを追加します。
• $table->text('image'):image というテキスト型のカラムを追加します(例えば、画像のURLなどを格納する場合などに使います)。
• $table->timestamps():created_at(作成日時)と updated_at(更新日時)のカラムを自動的に追加します。
public function down(): void
{
Schema::dropIfExists('posts');
}
Schema::dropIfExists('posts'):もし posts テーブルが存在していれば、それを削除します。マイグレーションのロールバック(変更を元に戻す)を行うために使います。
2. ビューの作成
続いてResources/views/postの create.blade.php に以下のコードを書く
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
フォームですよ
</h2>
</x-slot>
<div class="max-w-7xl mx-auto px-6">
@if(session('message'))
<div class="text-red-600 font-bold">
{{session('message')}}
</div>
@endif
<form method="post" action="{{ route('post.store') }}" enctype="multipart/form-data">
@csrf
<div class="mt-8">
<div class="w-full flex flex-col">
<label for="title" class="font-semibold mt-4">件名</label>
<x-input-error :messages="$errors->get('title')" class="mt-2" />
<input type="text" name="title" class="w-full py-2 px-3 border border-gray-300 rounded-md" id="title" placeholder="件名を入力してください"
value="{{old('title')}}">
</div>
</div>
<div class="w-full flex flex-col">
<label for="body" class="font-semibold mt-4">本文</label>
<x-input-error :messages="$errors->get('body')"class="mt-2"/>
<textarea name="body" class="w-full py-2 px-3 border border-gray-300 rounded-md" id="body" cols="30" rows="5" placeholder="本文を入力してください">
{{old('body')}}
</textarea>
</div>
<label for="image">画像:</label>
<input id="image" type="file" name="image">
<x-primary-button type="submit" class="mt-4">
送信する
</x-primary-button>
</form>
</div>
</x-app-layout>
以下説明
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
フォームですよ
</h2>
</x-slot>
• x-app-layout は、Laravelのコンポーネントを利用してページ全体のレイアウトを定義します。
• x-slot name="header" は、ページのヘッダー部分に表示する内容を定義しています。ここでは「フォームですよ」という見出しが表示されます。
@if(session('message'))
<div class="text-red-600 font-bold">
{{ session('message') }}
</div>
@endif
• session('message') で、セッションに保存されたメッセージ(例えば、フォーム送信後の成功メッセージ)を表示しています。
• メッセージがあれば、赤い文字でメッセージが表示されます。
<form method="post" action="{{ route('post.store') }}" enctype="multipart/form-data">
@csrf
• method="post" はフォーム送信のHTTPメソッドとしてPOSTを使用します。
• action="{{ route('post.store') }}" は、フォームの送信先を指定します。ここでは post.store という名前のルートに送信されます(コントローラーの store メソッド)。
• @csrf はCSRF(クロスサイトリクエストフォージェリ)保護用のトークンを生成します。
method="post"の説明
method="post"は、HTMLのフォームタグで使用する属性で、フォームが送信される際に使用するHTTPメソッドを指定します。
HTTPメソッド
HTTPメソッドにはいくつかの種類がありますが、主に使うのは以下の2つです:
• GET: データを取得するためのリクエストです。フォームを送信する際、データはURLの一部として送信されます(URLの後ろにクエリパラメータとして追加されます)。
• POST: データをサーバーに送信するためのリクエストです。フォームを送信する際、データはリクエストの本文に含まれ、URLに表示されません。
method="post"が使われる理由
method="post"を使うと、ユーザーが入力したデータ(タイトル、本文、画像など)が安全にサーバーに送信されます。例えば、ユーザーが記事を投稿する場合、その内容をサーバーに送信し、データベースに保存したり、処理を行ったりします。
• POSTメソッドは、データを送信する際にURLに表示されず、リクエストの「ボディ」にデータが含まれるため、GETメソッドよりもセキュアです。
• また、ファイルアップロード(画像のような)を行う場合には、method="post"とenctype="multipart/form-data"を指定しなければなりません。
<div class="mt-8">
<div class="w-full flex flex-col">
<label for="title" class="font-semibold mt-4">件名</label>
<x-input-error :messages="$errors->get('title')" class="mt-2" />
<input type="text" name="title" class="w-full py-2 px-3 border border-gray-300 rounded-md" id="title" placeholder="件名を入力してください"
value="{{ old('title') }}">
</div>
</div>
• label for="title":
• このタグは、フォームの「件名」フィールドにラベルを付けるために使います。**for="title"**は、このラベルがどのフォームフィールドに対応しているかを指定しており、titleという名前の入力フィールドに関連づけられます。
• つまり、このラベルをクリックすると、タイトルの入力フィールドにフォーカスが移ります。
•x-input-error :messages="$errors->get('title'):
• これはLaravelのカスタムコンポーネントで、入力フィールドにエラーがあった場合にエラーメッセージを表示するためのものです。
•errors->get('title') は、もし「件名」フィールドにバリデーションエラーがあった場合、そのエラーメッセージを取得して表示します。
• input type="text" name="title" ... value="{{ old('title') }}">:
•<input タグは、実際のユーザー入力を受け取るためのテキストフィールドです。
• name="title" は、このフィールドが送信されるデータの中で「title」という名前で値を送信することを意味します。
• value="{{ old('title') }}" は、フォームを送信した後、エラーがあった場合でも、前回入力された「タイトル」の値を再表示するためのものです。これにより、ユーザーは再度入力する手間を省けます。
Controllerの作成
app/Http/Controllers/PostControllerを作成していく、なお上ではcreate部分のことしか話していないが、ここではCRUD全てのことについて触れる。
このコードは、Laravelで作成されたPostControllerというコントローラーの内容です。主に「投稿」に関連する操作(CRUD)を扱っており、Postモデルに基づいてデータを操作します。以下に、各メソッドの詳細な説明を行います。
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Request;
use App\Models\Post;
class PostController extends Controller
{
public function index(){
//postsデータ
$posts=Post::all();
return view('post.index', compact('posts'));
}
public function create()
{
return view('post.create');
}
public function store(Request $request){
Gate::authorize('test');
$validated = $request->validate([
'title'=>'required|max:20',
'body'=>'required|max:400',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'// 画像バリデーション
]);
$validated['user_id']=auth()->id();
if ($request->hasFile('image')) {
$validated['image'] = $request->file('image')->store('images', 'public'); // `storage/app/public/images` に保存
}
$post = Post::create($validated);
$request->session()->flash('message','保存しました');
return back();
}
//タイプヒント
public function show(Post $post){
return view('post.show',compact('post'));
}
public function edit(Post $post){
return view('post.edit',compact('post'));
}
public function update(Request $request, Post $post){
$validated = $request->validate([
'title'=>'required|max:20',
'body'=>'required|max:400',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'// 画像バリデーション
]);
$validated['user_id']=auth()->id();
if ($request->hasFile('image')) {
// 既存の画像を削除
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
// 新しい画像を保存
$validated['image'] = $request->file('image')->store('images', 'public');
}
$post->update($validated);
$request->session()->flash('message','更新しました');
return back();
}
public function destroy(Request $request,Post $post){
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
$post->delete();
//sessionは一時保存
$request->session()->flash('message','削除しました');
return redirect()->route('post.index');
}
}
以下説明
public function index(){
$posts = Post::all();
return view('post.index', compact('posts'));
}
• 目的: すべての投稿を表示するためのメソッド。
• 処理内容: Post::all()で、postsテーブルの全データを取得します。それをpost.indexビューに渡して、表示します。
public function store(Request $request){
Gate::authorize('test');
$validated = $request->validate([
'title'=>'required|max:20',
'body'=>'required|max:400',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'
]);
$validated['user_id'] = auth()->id();
if ($request->hasFile('image')) {
$validated['image'] = $request->file('image')->store('images', 'public');
}
$post = Post::create($validated);
$request->session()->flash('message', '保存しました');
return back();
}
• 目的 : 新しい投稿をデータベースに保存するためのメソッド。
• 処理内容 :
-
Gate::authorize('test'): この行は、ユーザーがtestという権限を持っているか確認します。権限に応じてアクセスを制限できます。
(Gateに関しては別記事で紹介) - $request->validate([...]) : バリデーションを行い、title(最大20文字)、body(最大400文字)、image(画像ファイルとして最大2MB)などを検証します。
(Requestは、HTTPリクエストに関する情報を管理するクラスです。$requestは、ユーザーがフォームを送信したときや、APIリクエストがあったときに、そのリクエストに関するデータを取得するために使われます。
例えば、ユーザーがフォームに入力したデータは、$requestを通じてアクセスできます。これには、フォームのデータ(title, body, 画像など)や、HTTPヘッダ、クッキー、セッションなど、リクエストに関連するすべての情報が含まれます。
Request $request: ユーザーから送信されたHTTPリクエストに関連するデータを取得するためのオブジェクトです。これを使ってフォームデータを操作します。)
(validatedは、バリデーション(入力検証)を通過したデータを格納するための変数です。具体的には、$request->validate() メソッドを使ってフォームのデータを検証した後、その検証を通過したデータがvalidatedに格納されます。
バリデーションは、ユーザーがフォームに入力したデータが正しいかどうかを確認するための処理です。例えば、titleが必須であり、最大文字数が20であることを確認したり、imageが画像ファイルであることを確認するなどです。
$request->validate() は、バリデーションを実行し、もしバリデーションが失敗した場合、自動的にエラーメッセージを返し、処理が中断されます。バリデーションに成功した場合、リクエストデータをvalidatedに格納し、それを後続の処理(データベース保存など)に使用します。)
-
$validated['user_id'] = auth()->id(); : 現在ログインしているユーザーのIDを投稿に関連づけます。
-
$request->hasFile('image') : もし画像ファイルがアップロードされていれば、storage/app/public/imagesに保存します。
-
最後に、Post::create($validated) で、検証されたデータを元に新しい投稿を作成します。
• Post::create() は、Postモデルのインスタンスを作成し、そのインスタンスをデータベースに挿入するメソッドです。
• $validated には、titleやbodyなど、フォームから送信された検証済みのデータが含まれています。
• このメソッドを実行すると、新しいレコードがpostsテーブルに追加されます。
• 返り値として作成されたPostモデルのインスタンス(新しい投稿オブジェクト)が返され、それが$post変数に格納されます。
$request->session()->flash('message', '保存しました');
- 成功した場合、セッションに「保存しました」というメッセージを設定し、フォームに戻します。
• session()->flash()メソッドを使って、一時的なセッションデータを設定します。これは、リダイレクト後に次のリクエストで利用できるデータです。
• 'message'はセッションキー、'保存しました'はその値です。これにより、次のページ読み込み時にセッションから「保存しました」というメッセージが取得できます。
- • フラッシュメッセージは一度しか表示されません。セッションが新しいリクエストに対して送信された後、消去されます。
public function show(Post $post){
return view('post.show', compact('post')); // 特定の投稿を表示
}
• 目的 : 投稿の詳細を表示します。
• 処理 :
• ルートパラメータで渡されたPostモデル($post)をビューに渡して表示します。
public function edit(Post $post){
return view('post.edit', compact('post')); // 特定の投稿を編集するフォームを表示
}
• 目的: 投稿を編集するためのフォームを表示します。
• 処理:
• Postモデルをビューに渡して、投稿の編集フォームを表示します。
public function update(Request $request, Post $post){
$validated = $request->validate([
'title' => 'required|max:20',
'body' => 'required|max:400',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'
]);
$validated['user_id'] = auth()->id(); // 更新するユーザーIDを設定
if ($request->hasFile('image')) {
// 既存の画像を削除
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
// 新しい画像を保存
$validated['image'] = $request->file('image')->store('images', 'public');
}
// 投稿データを更新
$post->update($validated);
// セッションメッセージを設定
$request->session()->flash('message', '更新しました');
return back();
}
• 目的: 投稿を更新します。
• 処理:
o バリデーションを行い、タイトル、本文、画像の検証を行います。
o 画像がアップロードされていれば、既存の画像を削除し、新しい画像を保存します。
o Post::update()でデータを更新し、セッションメッセージを設定します。
public function destroy(Request $request, Post $post){
if ($post->image) {
Storage::disk('public')->delete($post->image); // 投稿に画像があれば削除
}
$post->delete(); // 投稿を削除
$request->session()->flash('message', '削除しました'); // セッションメッセージ
return redirect()->route('post.index'); // 投稿一覧へリダイレクト
}
• 目的: 投稿を削除します。
• 処理:
• 投稿に画像があれば、それを削除します。
• Post::delete()で投稿を削除します。
• セッションに'削除しました'というメッセージを設定し、投稿一覧にリダイレクトします。