Laravel 入門 ⑤(タスクの表示と削除の機能を実装する)

皆さん、こんにちは。管理人です。今回は作成されたタスクの一覧をダッシュボードの画面に表示し、タスクを削除する機能も実装したいと思います。

作成されたタスクをダッシュボードに表示する

このセクションでは(ログイン中のユーザーによって)作成されたタスクのタイトル一覧がダッシュボードに表示されるようにしたいと思います。

まず、routes/web.php を開き、次のように編集して下さい。

<?php

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

/*
|--------------------------------------------------------------------------
| 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('/', function () {
    return view('welcome');
});

// 編集ここから
Route::get('/dashboard', [TaskController::class, 'show'])->middleware(['auth'])->name('dashboard');
// 編集ここまで

Route::post('/store', [TaskController::class, 'store'])->middleware(['auth'])->name('store');

require __DIR__.'/auth.php';

続いて、TaskController の show アクション(GET)を実装したいと思います。app/Http/Controllers/TaskController.php を次のように編集して下さい。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Task;

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

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

        return redirect()->route('dashboard'); // 処理が終わったらダッシュボードにリダイレクトします
    }

    // 追記ここから
    public function show()
    {
        $tasks = Task::where('user_id', \Auth::id())->get(); // ログイン中のユーザーが作成したタスクをすべてとってきます
        return view('dashboard', compact('tasks')); // ここではビューに変数を渡しています
    } // 追記ここまで
}

ここで 26 行目に「ビューに変数を渡」すという表現が出てきました。上の TaskController.php の 26 行目では、PHP の compact という関数を使って dashboard というビューに変数 $tasks を渡しているのです。そうすることによって、次の dashboard.blade.php 内で変数 $tasks を使うことができるようになります。

それでは、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-5">
        <div class="max-w-3xl mx-auto px-4">
            <form class="grid grid-cols-1 gap-3" method='POST' action="/store" enctype="multipart/form-data">
                @csrf
                <label class="block">
                    <span class="text-gray-700">新しいタスク</span>
                    <input name="title" type="text" class="mt-2 block w-full rounded-md border-gray-300 shadow-sm">
                </label>
                <button type='submit' class="w-20 bg-blue-600 hover:bg-blue-500 text-white rounded px-4 py-2">作成</button>
            </form>
            <!-- 追記ここから -->
            @foreach ($tasks as $task)
                <div>{{ $task['title'] }}</div>
            @endforeach
            <!-- 追記ここまで -->
        </div>
    </div>
</x-app-layout>

ここで

@foreach ($tasks as $task)
    <div>{{ $task['title'] }}</div>
@endforeach

という記述が出てきました。この foreach というのは、配列に含まれる各要素を順番に取り出して処理する際によく使います。ここでは、$tasks がログイン中のユーザーがもつタスクの全体をあらわしますので、変数 $task には、そこに含まれるタスクが順番に代入されることになります。また、二重括弧 {{ }} で変数を囲うと、その変数の値が出力されることになります。

それでは、php artisan serve と npm run dev というコマンドが実行中であることと XAMPP の MySQL が緑色になっていることを確認して、ブラウザで localhost:8000 にアクセスしてみましょう。

ダッシュボードを見てみると、前回 ④ の記事で作成したタスクのタイトルが表示されているのが確認できます。
新しいタスクを作っていくと、このように次々と表示されていくのが分かります。

タスクを削除する機能を実装する

次に、タスクを削除する機能を作っていきたいと思います。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-5">
        <div class="max-w-3xl mx-auto px-4">
            <form class="grid grid-cols-1 gap-3" method='POST' action="/store" enctype="multipart/form-data">
                @csrf
                <label class="block">
                    <span class="text-gray-700">新しいタスク</span>
                    <input name="title" type="text" class="mt-2 block w-full rounded-md border-gray-300 shadow-sm">
                </label>
                <button type='submit' class="w-20 bg-blue-600 hover:bg-blue-500 text-white rounded px-4 py-2">作成</button>
            </form>
            @foreach ($tasks as $task)
                <!-- 編集ここから -->
                <div class="flex flex-wrap justify-between p-2 my-5 bg-green-600 rounded">
                    <div class="text-white">{{ $task['title'] }}</div>
                    <div class="flex">
                        <button type='submit' class="w-20 bg-blue-600 hover:bg-blue-500 text-white rounded px-4 py-2 mr-2" onclick="location.href='#'">編集</button>
                        <form method='POST' action="/delete/{{ $task['id'] }}" enctype="multipart/form-data">
                            @csrf
                            <button type='submit' class="w-20 bg-red-600 hover:bg-red-500 text-white rounded px-4 py-2">削除</button>
                        </form>
                    </div>
                </div>
                <!-- 編集ここまで -->
            @endforeach
        </div>
    </div>
</x-app-layout>

ここでのポイントは 24 行目の

action="/delete/{{ $task['id'] }}"

です。このルーティングはまだ定義していませんが、こうすることにより、該当するタスクを削除するボタンとなります。

するとダッシュボードはこのような見た目になると思います。

次にこのルーティングを定義するため、routes/web.php に一行を加えて次のようにして下さい。

<?php

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

/*
|--------------------------------------------------------------------------
| 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('/', function () {
    return view('welcome');
});

Route::get('/dashboard', [TaskController::class, 'show'])->middleware(['auth'])->name('dashboard');
Route::post('/store', [TaskController::class, 'store'])->middleware(['auth'])->name('store');
Route::post('/delete/{id}', [TaskController::class, 'delete'])->middleware(['auth'])->name('delete'); // 追記

require __DIR__.'/auth.php';

ここで 23 行目に /delete/{id} と書かれていますが、この {id} の部分がパラメータとなり、削除するタスクの ID となります。同じく 23 行目に [TaskController::class, 'delete'] とありますので、TaskControllerdelete アクション(POST)を次のように定義しましょう。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Task;

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

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

        return redirect()->route('dashboard'); // 処理が終わったらダッシュボードにリダイレクトします
    }

    public function show()
    {
        $tasks = Task::where('user_id', \Auth::id())->get();
        return view('dashboard', compact('tasks'));
    }

    // 追記ここから
    public function delete($id)
    {
        /** この if 文がないと、他のユーザーのタスクを削除できてしまう可能性があります */
        if (Task::where('id', $id)->where('user_id', \Auth::id())->exists()) {
            Task::destroy($id); // クリックされた削除ボタンに対応するタスクを削除します
            return redirect()->route('dashboard'); // ダッシュボードにリダイレクトします
        } else {
            abort(500); // サーバーエラー
        }
    } // 追記ここまで
}

ここで、TaskController.php の delete アクションは

public function delete($id)
{
    Task::destroy($id);
    return redirect()->route('dashboard');
}

と書いてもよいのではないかと思う方もいると思います。しかしながらこれはちょっと危険です。理由は後述します。

そしてダッシュボードを再読み込みして、削除ボタンをクリックしてみて下さい。この画像は「かきくけこ」というタスクを削除ボタンで消したところです(phpMyAdmin から見ても「かきくけこ」というタイトルのタスクは消えているはずですので確認してみましょう)。
この画像は、phpMyAdmin で tasks テーブル(の中身)を表示したところです。念のため、他のユーザー(ログイン中のユーザーではないユーザー)のタスクを削除できないことを確認してみましょう。現在ログインしているユーザーの ID は 1 なのですが、ここではユーザー ID が 2 である赤枠で囲ったタスク(id が 3 のタスク)をターゲットにしてみましょう。
画像のようにダッシュボードにおいて、Ctrl + Shift + I キーで開発者ツールを開き、form タグの action="/delete/*" (* は数字)のところ(赤枠で囲ったところ)をダブルクリックして
action="/delete/3" としてみましょう。そして、書き換えたところに対応する削除ボタンをクリックすると
狙い通り SERVER ERROR となりました。
そして phpMyAdmin で tasks テーブルを確認してみて下さい。ターゲットにしたタスク(赤枠で囲ったもの)は残ったままだと思います。このようにするために、delete アクションの中に if – else 文を書きました。

最後に

いかがでしたでしょうか。次回は作成したタスクのタイトルを編集できるようにする機能を実装したいと思います。お疲れ様でした。

コメント

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