1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravel を使って簡単なCRUD機能を実装 ①

Last updated at Posted at 2025-03-14

Laravel を使って簡単なCRUD機能を実装(フォームの作成編)

スクリーンショット 2025-03-14 23.01.44.png
このようなフォームを作成することを目指す。

1. モデルとマイグレーションファイルの作成

まずデータベース部分すなわちモデルとマイグレーションファイルを作成していく。
次のコードを実行し、マイグレーションファイルを作成する。

sail artisan make:model Post -m

そして以下のコードを書く

(日付)_create_posts_table.php

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();
}

目的 : 新しい投稿をデータベースに保存するためのメソッド。
処理内容 :

  1. Gate::authorize('test'): この行は、ユーザーがtestという権限を持っているか確認します。権限に応じてアクセスを制限できます。
    (Gateに関しては別記事で紹介)
  2. $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に格納し、それを後続の処理(データベース保存など)に使用します。)

  1. $validated['user_id'] = auth()->id(); : 現在ログインしているユーザーのIDを投稿に関連づけます。

  2. $request->hasFile('image') : もし画像ファイルがアップロードされていれば、storage/app/public/imagesに保存します。

  3. 最後に、Post::create($validated) で、検証されたデータを元に新しい投稿を作成します。

    Post::create() は、Postモデルのインスタンスを作成し、そのインスタンスをデータベースに挿入するメソッドです。
    $validated には、titleやbodyなど、フォームから送信された検証済みのデータが含まれています。
    • このメソッドを実行すると、新しいレコードがpostsテーブルに追加されます。
    • 返り値として作成されたPostモデルのインスタンス(新しい投稿オブジェクト)が返され、それが$post変数に格納されます。

$request->session()->flash('message', '保存しました');

  1. 成功した場合、セッションに「保存しました」というメッセージを設定し、フォームに戻します。
    • session()->flash()メソッドを使って、一時的なセッションデータを設定します。これは、リダイレクト後に次のリクエストで利用できるデータです。

• 'message'はセッションキー、'保存しました'はその値です。これにより、次のページ読み込み時にセッションから「保存しました」というメッセージが取得できます。

  1. • フラッシュメッセージは一度しか表示されません。セッションが新しいリクエストに対して送信された後、消去されます。
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()で投稿を削除します。
• セッションに'削除しました'というメッセージを設定し、投稿一覧にリダイレクトします。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?