laragear/rewind

回到过去,查看模型的历史状态。

v1.0.0 2024-07-30 02:17 UTC

This package is auto-updated.

Last update: 2024-08-30 02:26:05 UTC


README

Latest Version on Packagist Latest stable test run Codecov coverage CodeClimate Maintainability Sonarcloud Status Laravel Octane Compatibility

回到过去,查看模型的历史状态,并一键恢复。

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许可证授权。

LaravelTaylor Otwell的商标。版权所有 © 2011-2024 Laravel LLC。