0
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でUndo/Redo機能を実装

Last updated at Posted at 2024-12-23

経緯

先日実装したUndo/Redo機能(元に戻す/やり直す)がそれなりの工数がかかったので、
今後のためにざっくり残します。

スタッフのシフトを管理するWebアプリケーションに実装しました。
本記事ではバックエンド側の実装のみ残します。

※以前の関連記事
Vue.js3で「ctrl+z」「ctrl+shift+z」の処理を実装する

用語定義

  • 履歴
    • 画面操作した時のテーブルのレコードを保存したもの
  • 元に戻す(Undo)
    • 操作を過去に戻す↩️ 一つ過去の履歴を適用する
  • やり直す(Redo)
    • 操作を先に進める↪️ 一つ先の履歴を適用する

動作のイメージ

まず初めにブラウザ更新をして履歴1が作成され、何かしらの操作を3回行ったとする。
そうすると履歴2〜4が作成され、履歴4が適用されている状況になる。

first.png

仮に「元に戻す」を3回実行すると、履歴が過去へ戻り、履歴1が適用されている状況になる。

undo.png

更にここで「やり直す」を1回実行すると、履歴が先に進み、履歴2が適用されている状況になる。

redo.png

この状況で新たに操作を実行すると、履歴3以降が削除されて、新たな履歴(履歴3')が適用された状態になる。

do_again.png

※ブラウザが更新されたタイミングで、履歴が全て削除されて、履歴1から始まる。

Undo/Redo機能概要

履歴をリセットする

  • 保存してある履歴を全て削除、テーブルからdeleteする
  • ブラウザ更新された際に実行する

履歴を保存する

  • その時の状態を履歴として保存、テーブルにinsertする
  • 画面上で操作が発生した際に実行する

操作を元に戻す・やり直す

  • 保存された履歴、テーブルからselectして、過去へ戻す or 先へ進める
  • Undo/Redoボタン押下時に実行する

やり直す対象の履歴をリセットする

  • やり直す対象の履歴を全て削除、テーブルからdeleteする
    • 過去の履歴は残す
  • 画面上で操作が発生した際に実行する

テーブル構成

既存テーブル

  • users
カラム
id int PK
name varchar
  • staffs
カラム
id int PK
name varchar
  • shifts
カラム
id int PK
date datetime
  • shift_staffs
カラム
id int PK
shift_id int FK
staff_id int FK
start_time int
end_time int

履歴テーブル ※今回実装

  • histories
カラム
id int PK
user_id int FK
status int
  • history_shift_staffs
カラム
id int PK
history_id int FK
shift_id int FK
staff_id int FK
start_time int
end_time int

対象のテーブルが増える場合は、history_*テーブルを作成して、history_idをFKにする
※今回実際はhistory_shift_staffsの子テーブルが存在していたので、その場合はhistory_idではなくhistory_shift_staff_idをFKにしました

実装

具体的なコードは、いったんHistoryモデルとHistoryServiceクラスのみgithubに公開します。
※いずれ時間を取れたら整理したい…
https://github.com/shinji-yoshino/undo_redo_laravel

Historyモデル

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class History extends Model
{
   use HasFactory;

   const CURRENT = 1;
   const UNDO_TARGET = 2;
   const REDO_TARGET = 3;

   /**
    * The attributes that are mass assignable.
    *
    * @var array<int, string>
    */
   protected $fillable = [
       'user_id',
       'status',
   ];

   public function history_shift_staffs(): HasMany
   {
       return $this->hasMany(HistoryShiftStaff::class);
   }

}

HistoryServiceクラス

コード量が比較的多いので、ここではpublicメソッドの説明だけ書きます。

  • create_history()
    • 履歴を作成するメソッドで、操作が発生するたびに呼び出される
  • copy_history_to_target()
    • 履歴からシフト・画面に反映させる処理で、いわゆるUndo/Redo処理を行う
  • exist_undo_target()
    • Undo対象の履歴が存在するか確認、Undoボタンの制御に使用
  • exist_redo_target()
    • Redo対象の履歴が存在するか確認、Redoボタンの制御に使用

参考記事

0
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
0
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?