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で安全な「戻る」ボタンを実装する(一覧⇄詳細⇄編集)

Posted at

概要

Laravelアプリで、

▪️ 一覧ページ(indexやoneday)

▪️ 詳細ページ(show)

▪️ 編集ページ(edit)

を行き来する際に、「元の一覧ページ」に確実に戻れるボタンの実装方法です。
さらに、外部URLをブロックしてセキュリティを確保します。

なぜ必要か

▪️ URL::previous() やブラウザの戻るボタンは、リロードや別タブ操作で崩れやすい

▪️ 一覧の種類(index or oneday)を保持したい
  index、oneday両方とも同じshowへ移動できる場合の戻る処理

▪️ 悪意ある外部サイトに飛ばされるオープンリダイレクトを防止したい

実装の流れ

1. 一覧 or onedayから詳細へ back_url を付けて遷移

同じaタグ

// /resources/views/tasks/one_day.blade.php
<a href="{{ route('tasks.show', ['task' => $task->id, 'back_url' => url()->full()]) }}" 
    class="block transition-all duration-200 rounded hover:shadow-lg hover:-translate-y-0.5">


// /resources/views/tasks/index.blade.php
<a href="{{ route('tasks.show', ['task' => $task->id, 'back_url' => url()->full()]) }}" 
    class="block transition-all duration-200 rounded hover:shadow-lg hover:-translate-y-0.5">

2. 詳細(show)で back_url を受け取り、外部URLをブロック

/app/Http/Controllers/TaskController.php
    public function show(Request $request, $id)
    {
        // ----- ユーザー情報取得
        /** @var \App\Models\User $user */
        $user = Auth::user();

        // ----- タスク情報取得
        $task = $user->tasks()
            ->with('taskCategory')  
            ->findOrFail($id);

        // ----- 外部URLを弾く処理
        $defaultUrl = route('tasks.index');
        $backUrl = $request->query('back_url', $defaultUrl);

        // 外部URLブロック
        if(!Str::startsWith($backUrl, config('app.url'))) {
            $backUrl = $defaultUrl;
        }

        return view('tasks.show', compact('task', 'backUrl'));
    }

3. 詳細→編集で back_url を引き継ぐ

// /resources/views/tasks/show.blade.php
<a href="{{ route('tasks.edit', ['task' => $task->id, 'back_url' => $backUrl ]) }}"
    class=" text-white text-center bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg">
    編集
</a>

4. 編集(edit)で showに戻るためのURLを作成

/app/Http/Controllers/TaskController.php
public function edit(Request $request, $id)
{
    // ----- ユーザー情報取得
    /** @var \App\Models\User $user */
    $user = Auth::user();

    // ----- タスク情報取得
    $task = $user->tasks()
        ->with('taskCategory')  
        ->findOrFail($id);

    // ----- フォーカスマトリックス情報取得
    $taskCategories = TaskCategory::get();

    // ---- one-day or index への戻るボタン
    $defaultUrl = route('tasks.index');
    $backUrl = $request->query('back_url', $defaultUrl);

    // 外部URLブロック
    if(!Str::startsWith($backUrl, config('app.url'))) {
        $backUrl = $defaultUrl;
    }

    // ----- showへ戻る専用URL(一覧URLを持ち回り)
    $showUrl = route('tasks.show', ['task' => $task->id, 'back_url' => $backUrl]);

    return view('tasks.edit', compact('task', 'taskCategories', 'backUrl', 'showUrl'));
}

5. 編集画面に「詳細へ戻る」リンクと更新フォーム

// /resources/views/tasks/edit.blade.php
<form action="{{ route('tasks.update', ['task' => $task->id]) }}" method="POST">
    @csrf
    @method('PUT')
    <input type="hidden" name="back_url" value="{{ $backUrl ?? request('back_url') }}">

6. 更新後に show にリダイレクト(一覧URLを保持)

/app/Http/Controllers/TaskController.php
public function update(Request $request, $id)
{

    // ----- ユーザー情報取得
    /** @var \App\Models\User $user */
    $user = Auth::user();

    // ----- 時間
    // フォームの date + time を結合
    $startAt = Carbon::parse(
        $request->start_date.' '.($request->start_time),
    );
    $endAt = Carbon::parse(
        $request->end_date.' '.($request->end_time),
    );

    // 締切が開始より前ならエラー(after_or_equal相当)
    if($endAt->lt($startAt)) {
        return back()
            ->withErrors(['end_date' => '締切は開始以降にしてください。'])
            ->withInput();
    }

    // ----- タスク情報取得
    $task = $user->tasks()
        ->findOrFail($id);

    // ----- 保存
    $task->update([
        'user_id' => $user->id,
        'task_category_id' => $request->task_category,
        'title' => $request->title,
        'description' => $request->description,
        'start_at' => $startAt,
        'end_at' => $endAt,
        'is_completed' => $task->is_completed,
    ]);

    // ----one-day or index への戻るボタン
    $backUrl = $request->back_url;

    // ----- リダイレクト
    return to_route('tasks.show', ['task' => $task->id, 'back_url' => $backUrl])->with('success', 'タスクの更新完了しました。');
}

セキュリティ対策(外部URLブロック)

back_url はユーザーから渡される値なので、そのまま使うと危険。
オープンリダイレクトを防ぐため、アプリURLから始まるかをチェックする。

/app/Http/Controllers/TaskController.php
if(!Str::startsWith($backUrl, config('app.url'))) {
    $backUrl = $defaultUrl;
}
// 以下で使用
// public function show(Request $request, $id)
// public function edit(Request $request, $id)
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?