laragear / rewind
回到过去,查看模型的历史状态。
Requires
- php: ^8.1
- illuminate/database: 10.*|11.*
- illuminate/support: 10.*|11.*
- laragear/meta-model: ^1.1
Requires (Dev)
- orchestra/testbench: 8.*|9.*
README
回到过去,查看模型的历史状态,并一键恢复。
use App\Models\Article; $article = Article::find(1); $article->rewind()->toLatest();
成为赞助商
您的支持使我能够保持此包免费、更新和可维护。或者,您可以 传播这个词!
要求
调用 Composer 获取包。
composer require laragear/rewind
设置
首先,安装迁移文件。此迁移将在数据库中创建一个表,用于保存模型的所有历史状态。
php artisan vendor:publish --provider="Laragear\Rewind\RewindServiceProvider" --tag="migrations"
提示
您可以在迁移之前通过添加新列来 编辑迁移,并且还可以 更改表名。
php artisan migrate
最后,将 HasRewind
特性添加到您想要保存状态的模型中。
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laragear\Rewind\HasRewind; class Article extends Model { use HasRewind; // ... }
就是这样。下次您想恢复模型的前一个状态时,请使用 rewind()
方法。
use App\Models\Article; $article = Article::find(1); $article->rewind()->toLatest();
它是如何工作的?
当模型被创建并随后更新时,一个新的 状态 将被保存到数据库中。该状态由原始模型的原始属性组成。
使用 rewind()
辅助方法,您可以轻松查看和恢复先前的模型状态,例如最后一个,或者有一个列表可供恢复。此外,HasRewind
特性允许控制 何时 保存状态,以及 保存和恢复什么。
状态是 反应式 的,而不是 主动式 的。换句话说,状态是在原始模型保存后保存的,而不是在保存之前。
保存状态
状态在模型创建或更新时自动创建。您不需要做任何事情来确保状态已持久化,但您可以通过 监听 StatePushed
事件。
use App\Models\Article; use Illuminate\Http\Request; public function store(Request $request) { $validated = $request->validate([ // ... ]); return Article::create($validated); // First state created automatically. } public function update(Article $article, Request $request) { $validated = $request->validate([ // ... ]); $article->update($validated); // State pushed automatically. return $article; }
不创建状态
有时您可能希望避免在模型创建或更新时创建副本。
为此,请使用模型的 withoutCreatingStates()
方法并使用回调。在回调内部,状态不会推送到数据库。
use App\Models\Article; use Illuminate\Http\Request; public function store(Request $request) { $validated = $request->validate([ // ... ]); // Create an article but don't save the first state. return Article::withoutCreatingStates(fn() => Article::create($validated)); }
手动创建状态
如果您已禁用 自动状态创建,则可以使用 create()
方法手动保存状态。
use App\Models\Article; use Illuminate\Http\Request; public function store(Request $request) { $validated = $request->validate([ // ... ]); $article = Article::create($validated); $article->rewind()->create(); // Save the first state. return $article; } public function update(Article $article, Request $request) { $validated = $request->validate([ // ... ]); $article->update($validated); $article->rewind()->create(); // Push a new state. return $article; }
create()
方法允许 保留状态 以避免自动修剪,并跳过自动修剪。
$article->rewind()->create( keep: true, prune: false, );
如果您想,可以不更新原始模型就保存状态。例如,您可以创建一系列文章“草案”。
public function draft(Article $article, Request $request) { $validated = $request->validate([ // ... ]); // Push a state from the updated article without persisting it. $article->fill($validated)->rewind()->create(); return back(); }
列出状态
要获取所有先前模型状态的列表,请使用 all()
方法。它将返回所有过去模型的 Eloquent 集合。
use App\Models\Article; $pastArticles = Article::find(1)->rewind()->all();
提示
您可以使用状态 ID 在以后 恢复给定的状态 ID。
计数
要计数所有保存的状态,请使用 count()
方法。它将返回持久化状态的数量。
use App\Models\Article; $count = Article::find(1)->rewind()->count();
存在性
要避免计数所有状态,只需检查是否至少为模型创建了一个状态,请使用 exists()
方法。
use App\Models\Article; $hasAtLeastOneState = Article::find(1)->rewind()->exists();
另外,missing()
方法用于检查模型是否有保存的状态。
use App\Models\Article; $hasNoStates = Article::find(1)->rewind()->missing();
最新和最旧状态
如果需要找到第一个或最后一个模型状态,也可以使用 findLatest()
和 findOldest()
。
use App\Models\Article; $latest = Article::find(1)->rewind()->getLatest(); $oldest = Article::find(1)->rewind()->getOldest();
检索状态 ID
要按状态 ID 检索模型实例,请使用 find()
方法。
use App\Models\Article; $pastArticle = Article::find(1)->rewind()->find(456);
注意
因为预期状态 ID 存在,如果 ID 不存在,将抛出 ModelNotFoundException
。
恢复状态
将以前的状态数据恢复到相同的模型实例的最简单方法是将 ID 与 to()
方法一起使用,然后只调用 save()
以在数据库中持久化更改。
use App\Models\Article; $article = Article::find(1); $article->title; // "Happy cavations!" $article->rewind()->to(468)->save(); $article->title; // "Happy vacations!"
或者,您也可以使用 toLatest()
或 toOldest()
分别恢复模型到最新或最旧状态。
use App\Models\Article; Article::find(1)->rewind()->toLatest()->save(); Article::find(1)->rewind()->toOldest()->save();
重要
当恢复的模型被更新时,它将创建一个新的状态。为了避免这种情况,请使用 withoutCreatingStates()
。
与原始模型一起恢复状态
如果您检索了以前的模型状态,您的代码中将实际上有两个实例:当前的实例和过去的状态模型。
保存过去的状态将替换数据库中原始实例的数据。原始实例不会意识到所做的更改,因此您应该刷新模型或丢弃它。
use App\Models\Article; $stale = Article::find(1); $stale->title; // "Happy vatacions!" $past = $original->rewind()->getLatest(); $past->save(); $stale->title; // "Happy vatacions!" $stale->fresh()->title; // "Happy vacations!"
删除状态
您可以通过 delete()
和状态的 ID 删除一个状态。
use App\Models\Article; Article::find(1)->rewind()->remove(765);
您还可以使用 deleteLatest()
或 deleteOldest()
分别删除最新或最旧状态。
use App\Models\Article; $article = Article::find(1); $article->rewind()->removeLatest(); $article->rewind()->removeOldest();
重要
使用 deleteLatest()
和 deleteOldest()
不会删除保留的状态,您需要发出 true
来强制其删除。
Article::find(3)->rewind()->deleteOldest(true);
删除所有状态
您可以通过调用 clear()
方法从模型中删除所有状态。
use App\Models\Article; Article::find(1)->rewind()->clear();
由于这不会包括 保留的状态,您可以使用 forceClear()
来包括它们。
use App\Models\Article; Article::find(1)->rewind()->forceClear();
修剪状态
每次模型更新时,它都会自动修剪旧模型状态以保持有限数量的状态。如果您已禁用它,则可能需要手动调用 prune()
方法以删除过时的状态。
use App\Models\Article; Article::find(1)->rewind()->prune();
注意
在检索状态时,将要修剪的状态将自动从查询中排除。
事件
此包触发以下事件
例如,您可以在您的应用程序中通过 监听器监听 StatePushed
事件。
use Laragear\Rewind\Events\StateCreated; class MyListener { public function handle(StateCreated $event) { $event->model; // The target model. $event->state; // The RewindState Model with the data. } }
为了方便,还包括了 StateRestored
事件。如果您决定将模型恢复到以前的状态,您可以使用 StateRestored::dispatch()
手动触发它,并使用模型实例。
use App\Models\Article; use Illuminate\Support\Facades\Route; use Laragear\Rewind\Events\StateRestored; Route::post('article/{article}/restore', function (Article $article) { $article->rewind()->toOldest()->save(); StateRestored::dispatch($article); return back(); });
原始状态
模型状态数据保存到 Laragear\Rewind\Models\RewindState
模型。此模型主要负责从它持有的原始数据创建新的模型实例。
查询原始状态
使用 query()
方法开始对给定模型的查询。例如,您可以使用此方法进行分页。
use App\Models\Article; $page = Article::find(1)->rewind()->query()->paginate();
您还可以直接使用模型实例查询进行进一步的自定义查询。
use Laragear\Rewind\Models\RewindState; RewindState::query()->where('created_at', '<', now()->subMonth())->delete();
配置
有时您可能想要更改模型上的回滚过程的工作方式。您可以使用特质方法配置何时以及如何保存状态。
限制保存状态的数量
默认情况下,回滚状态的限制为10个,无论它们有多久。您可以通过在rewindLimit()
方法中返回一个整数来更改数量。
public function rewindLimit() { return 10; }
您还可以将限制更改为一个时间点。在给定时间之前创建的状态在查询中不会被考虑,并在保存模型时自动修剪。
public function rewindLimit() { return now()->subMonth(); }
要同时通过数量和日期限制,请返回一个包含两者的数组。
public function rewindLimit() { return [10, now()->subMonth()]; }
最后,您可以通过返回假值来禁用限制,这将确保保存所有状态。
public function rewindLimit() { return false; }
自动状态保存
每次您创建或更新模型时,都会在数据库中创建一个新的状态。您可以使用shouldCreateRewindStateOnCreated()
和shouldCreateRewindStateOnUpdated()
分别更改此行为。
public function shouldCreateRewindStateOnCreated(): bool { // Don't save states if the article body is below a certain threshold. return strlen($this->body) < 500; } public function shouldCreateRewindStateOnUpdated(): bool { // Only let subscribed users to have states saved. return $this->user->hasSubscription(); }
如果您想不保存模型状态,这可能很有用,例如,如果文章正文没有足够的字符,或者用户没有订阅计划。
自动修剪
默认情况下,每次将新状态推入数据库时,都会修剪旧状态。您可以使用shouldPruneOldRewindStatesOnUpdated()
程序化地禁用此功能。
public function shouldPruneOldRewindStatesOnUpdated(): bool { return true; }
保留状态
当您创建一个模型时,后续推送新的状态后,初始状态可能会丢失。为了避免这种情况,您可以使用shouldKeepFirstRewindState()
始终保护第一个状态或任何状态。
public function shouldKeepFirstRewindState(): bool { return true; }
提示
如果第一个状态被保护,则它不会被修剪,因此只有较新的状态会旋转。
要保存到状态中的属性
默认情况下,模型的所有原始属性都会添加到状态数据中。您可以通过设置自己的属性来覆盖此操作,以便在状态中保存,通过返回一个关联数组或一个\Illuminate\Contracts\Support\Arrayable
实例。
use Illuminate\Contracts\Support\Arrayable; public function getAttributesForRewindState(): Arrayable|array { return [ 'author_id' => $this->author_id, 'title' => $this->title, 'slug' => $this->slug, 'body' => $this->body, 'private_notes' => null, 'published_at' => $this->published_at, ]; }
从状态中恢复的属性
要修改模型应如何从状态中填充属性,请使用setAttributesFromRewindState()
。它接收状态中的原始属性作为数组。
use Illuminate\Support\Arr; public function setAttributesFromRewindState(array $attributes): void { $this->setRawAttributes(Arr::except($attributes, 'excerpt')); }
迁移
Laravel Octane 兼容性
- 没有使用过时的应用程序实例的单例。
- 没有使用过时的配置实例的单例。
- 没有使用过时的请求实例的单例。
- 没有在每个请求上写入静态属性。
使用此包与Laravel Octane应该没有问题。
安全
如果您发现任何安全相关的问题,请通过电子邮件darkghosthunter@gmail.com而不是使用问题跟踪器。
许可证
此特定软件包版本根据发布时的MIT许可证授权。
Laravel是Taylor Otwell的商标。版权所有 © 2011-2024 Laravel LLC。