Laravel で電子掲示板を作ろう ③(記事を削除する機能の追加など)

皆さん、こんにちは。管理人です。このシリーズ「Laravel で電子掲示板を作ろう」では、その名の通り、Laravel を用いて電子掲示板を作っていこうと思っています。今回は第3回目となります。

トップページに全ユーザーの記事の一覧を載せる

それでは、ログインしていないユーザーでもどんな記事があるかが分かるように、トップページにすべてのユーザーが作成した記事の一覧を表示させたいと思います。routes/web.php の

Route::get('/', function () {
    return view('welcome');
});

という記述を削除して次のように修正して下さい。

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;

/*
|--------------------------------------------------------------------------
| 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('/', [PostController::class, 'index'])->name('index'); // 修正後
Route::get('/post/{post_id}', [PostController::class, 'post'])->name('post');

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified'
])->group(function () {
    Route::get('/dashboard', [PostController::class, 'show'])->name('dashboard');
    Route::get('/create', function () {
        return view('create');
    })->name('create');
    Route::post('/store', [PostController::class, 'store'])->name('store');
    Route::get('/edit/{post_id}', [PostController::class, 'edit'])->name('edit');
    Route::post('/update/{post_id}', [PostController::class, 'update'])->name('update');
});

続いて、PostController に次のように index アクションを追加します。(今回は上のほうにアクションを追加しました。)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;
use Auth;

class PostController extends Controller
{
    /**
     * このアクションを追加
     */
    public function index()
    {
        $posts = Post::latest('updated_at')->get();
        return view('welcome', compact('posts'));
    }

    public function store(Request $request)
    {
        $data = $request->all(); // フォームで送信されたデータをすべてとってきます

        $post_id = Post::insertGetId([
            'user_id' => Auth::id(), // ログイン中のユーザーの ID を格納します
            'title' => $data['title'], // 入力された文字列を格納します
            'content' => $data['content'], // 入力された文字列を格納します
        ]);

        return redirect()->route('post', compact('post_id'));
    }

    public function post($post_id)
    {
        $post = Post::where('id', $post_id)->first();
        if (is_null($post)) {
            abort(404);
        }
        return view('post', compact('post'));
    }

    public function show()
    {
        $posts = Post::where('user_id', Auth::id())->latest('updated_at')->get();
        return view('dashboard', compact('posts'));
    }

    public function edit($post_id)
    {
        $post = Post::where('id', $post_id)
                    ->where('user_id', Auth::id()) // ログイン中のユーザーが編集しようとしていることを確認
                    ->first();

        return view('edit', compact('post'));
    }

    public function update(Request $request, $post_id)
    {
        $data = $request->all();

        $query = Post::where('id', $post_id)->where('user_id', Auth::id());

        // ログイン中のユーザーが記事を更新しようとしていることを確認
        if ($query->exists()) {
            $query->update(['title' => $data['title'], 
                            'content' => $data['content']]);
            return redirect()->route('post', compact('post_id')); // 該当の記事にリダイレクト
        } else {
            abort(500); // サーバーエラー
        }
    }
}

そして、resources/views/welcome.blade.php を次のように全面的に書き換えます。

<x-guest-layout>
    <h1>Laravel 掲示板</h1>
    <p>これはLaravelで作った電子掲示板です。</p>
    <h2>みんなの記事一覧</h2>
    <table class="table-auto border-solid border-black border-2" style="border-collapse: separate; border-spacing: 0">
        <tr class="bg-green-300">
            <th class="border border-black px-4 py-2 text-center">タイトル</th>
            <th class="border border-black px-4 py-2 text-center">内容</th>
        </tr>
        @foreach ($posts as $post)
            <tr>
                <td class="border border-black px-4 py-2">
                    <a href="{{ route('post', $post['id']) }}" class="text-blue-500">{{ $post['title'] }}</a>
                </td>
                <td class="border border-black px-4 py-2">{{ Str::limit($post['content'], 80, '...') }}</td>
            </tr>
        @endforeach
    </table>
</x-guest-layout>
そしてブラウザで http://localhost:8000 にアクセスするとこのようなものが表示されるかと思います。タイトルをクリックすると該当の記事に飛ぶようになっています。

記事を削除する機能の追加

次に、ログイン中のユーザーが自分で作成した記事を削除できるようにしたいと思います。routes/web.php に一行を追加して次のようにします。

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;

/*
|--------------------------------------------------------------------------
| 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('/', [PostController::class, 'index'])->name('index');
Route::get('/post/{post_id}', [PostController::class, 'post'])->name('post');

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified'
])->group(function () {
    Route::get('/dashboard', [PostController::class, 'show'])->name('dashboard');
    Route::get('/create', function () {
        return view('create');
    })->name('create');
    Route::post('/store', [PostController::class, 'store'])->name('store');
    Route::get('/edit/{post_id}', [PostController::class, 'edit'])->name('edit');
    Route::post('/update/{post_id}', [PostController::class, 'update'])->name('update');
    Route::post('/delete/{post_id}', [PostController::class, 'delete'])->name('delete'); // 追記
});

続いて、app/Http/Controllers/PostController.php に次のようにして delete アクションを追加します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;
use Auth;

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::latest('updated_at')->get();
        return view('welcome', compact('posts'));
    }

    public function store(Request $request)
    {
        $data = $request->all(); // フォームで送信されたデータをすべてとってきます

        $post_id = Post::insertGetId([
            'user_id' => Auth::id(), // ログイン中のユーザーの ID を格納します
            'title' => $data['title'], // 入力された文字列を格納します
            'content' => $data['content'], // 入力された文字列を格納します
        ]);

        return redirect()->route('post', compact('post_id'));
    }

    public function post($post_id)
    {
        $post = Post::where('id', $post_id)->first();
        if (is_null($post)) {
            abort(404);
        }
        return view('post', compact('post'));
    }

    public function show()
    {
        $posts = Post::where('user_id', Auth::id())->latest('updated_at')->get();
        return view('dashboard', compact('posts'));
    }

    public function edit($post_id)
    {
        $post = Post::where('id', $post_id)
                    ->where('user_id', Auth::id()) // ログイン中のユーザーが編集しようとしていることを確認
                    ->first();

        return view('edit', compact('post'));
    }

    public function update(Request $request, $post_id)
    {
        $data = $request->all();

        $query = Post::where('id', $post_id)->where('user_id', Auth::id());

        // ログイン中のユーザーが記事を更新しようとしていることを確認
        if ($query->exists()) {
            $query->update(['title' => $data['title'], 
                            'content' => $data['content']]);
            return redirect()->route('post', compact('post_id')); // 該当の記事にリダイレクト
        } else {
            abort(500); // サーバーエラー
        }
    }

    /**
     * このアクションを追加
     */
    public function delete($post_id)
    {
        $query = Post::where('id', $post_id)->where('user_id', Auth::id());

        // ログイン中のユーザーが記事を削除しようとしていることを確認
        if ($query->exists()) {
            Post::destroy($post_id);
            return redirect()->route('dashboard');
        } else {
            abort(500); // サーバーエラー
        }
    }
}

そして、resources/views/dashboard.blade.php を次のように修正します。

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}(あなたの記事一覧)
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <table class="table-auto border-solid border-black border-2" style="border-collapse: separate; border-spacing: 0">
                <tr class="bg-green-300">
                    <th class="border border-black px-4 py-2">タイトル</th>
                    <th class="border border-black px-4 py-2">内容</th>
                    <th class="border border-black px-4 py-2">更新日時</th>
                    <th class="border border-black px-4 py-2 bg-yellow-300" colspan="2">操作</th> <!-- 修正後 -->
                </tr>
                @foreach ($posts as $post)
                    <tr>
                        <td class="border border-black px-4 py-2 text-blue-500">
                            <a href="{{ route('post', $post['id']) }}">{{ $post['title'] }}</a>
                        </td>
                        <td class="border border-black px-4 py-2">{{ Str::limit($post['content'], 60, '…' ) }}</td>
                        <td class="border border-black px-4 py-2">{{ $post['updated_at'] }}</td>
                        <td class="border border-black px-4 py-2 text-blue-500"><a href="{{ route('edit', $post['id']) }}">編集</a></td>
                        <!-- 追記ここから -->
                        <td class="border border-black px-4 py-2">
                            <form method='POST' action="{{ route('delete', $post['id']) }}">
                                @csrf
                                <button class="text-red-700" onclick='return confirm("タイトルが「{{ $post->title }}」の記事を削除しますか?");'>削除</button>
                            </form>
                        </td>
                        <!-- 追記ここまで -->
                    </tr>
                @endforeach
            </table>
        </div>
    </div>
</x-app-layout>
するとダッシュボードはこのようになるかと思います。
どれかひとつ「削除」の文字をクリックするとこのようにメッセージが表示されますので、OKをクリックすると該当する記事が削除されます。

これで、CRUD のすべての機能が実装されました。

コメント

タイトルとURLをコピーしました