famdirksen/revisionable

无需思考即可为您的模型保留修订历史,作为Laravel的包使用

1.48 2024-08-28 08:12 UTC

README

Laravel 4.x Laravel 5.x Laravel 6.x Laravel 7.x Laravel 8.x Latest Version Downloads License

您的项目中是否想为任何模型保留修订历史,而不必为此做任何工作。只需从您的模型扩展revisionable,就可以立即实现这一点,并能够显示类似以下的历史记录

  • Chris 将标题从 'Something' 更改为 'Something else'
  • Chris 将类别从 'News' 更改为 'Breaking news'
  • Matt 将类别从 'Breaking news' 更改为 'News'

因此,您不仅可以看到发生的历史,还可以看到是谁做了什么,这样就有问责制。

Revisionable 是一个Laravel包,允许您无需思考即可为您的模型保留修订历史。有关背景和信息的更多信息,请参阅这篇文章

新功能待办事项

这是一个需要添加到包中的功能列表。

  • 删除特定用户的全部修订(GDPR - 被遗忘的权利)
  • 删除特定模型的全部修订
  • 从修订中删除特定字段(s)
  • 导出全部修订(GDPR - 查看数据的权利)

与第三方Auth / Eloquent扩展一起工作

Revisionable 支持

Revisionable 现在也可以用作特性,因此您的模型可以继续扩展Eloquent,或任何扩展Eloquent的类(如Ardent)。

安装

Revisionable可以通过composer安装,详细信息请参见packagist,这里。

要将此包添加到您的项目中,请运行以下命令

composer require famdirksen/revisionable

最后,您还需要在包上运行迁移(Laravel 5.x)

php artisan migrate --path=vendor/famdirksen/revisionable/src/migrations

对于Laravel 4.x用户

php artisan migrate --package=famdirksen/revisionable

如果您将大量迁移上下文(使用migrate:refresh),您可以执行的操作之一是将包中的迁移文件复制到您的app/database文件夹中,并将类名从CreateRevisionsTable更改为类似CreateRevisionTable(不带's',否则您将收到一个错误,表明有重复的类)

cp vendor/famdirksen/revisionable/src/migrations/2013_04_09_062329_create_revisions_table.php database/migrations/2013_04_09_062329_create_revisions_table.php

文档

实现

新的特性实现

对于您想要保留修订历史记录的任何模型,请在模型中包含revisionable命名空间并使用RevisionableTrait,例如,如果您正在使用另一个可启动的特性,请确保在模型中覆盖boot方法;

namespace MyApp\Models;

class Article extends Eloquent {
    use \Famdirksen\Revisionable\RevisionableTrait;

    public static function boot()
    {
        parent::boot();
    }
}

作为特性,revisionable现在可以与标准Eloquent模型一起使用,或与任何扩展Eloquent的类一起使用,例如Ardent

特性需要PHP >= 5.4

基于类的传统实现

新的基于特性的方法与现有的Revisionable安装兼容。您仍然可以使用以下安装说明,这实际上是为特性扩展一个包装器。

对于您希望保留修订历史记录的任何模型,请包含revisionable命名空间并扩展revisionable而不是eloquent,例如:

use Famdirksen\Revisionable\Revisionable;

namespace MyApp\Models;

class Article extends Revisionable { }

请注意,这也适用于命名空间模型。

实现说明

如果需要,您可以通过在模型中将$revisionEnabled设置为false来禁用修订。如果您想暂时禁用修订,或者您想创建自己的基础模型,该模型扩展了revisionable,所有模型都扩展了它,但您希望为某些模型关闭revisionable,这将很有用。

namespace MyApp\Models;

class Article extends Eloquent {
    use \Famdirksen\Revisionable\RevisionableTrait;

    protected $revisionEnabled = false;
}

您也可以通过将$historyLimit设置为停止修订之前的修订次数来在X多次修订后禁用修订。

namespace MyApp\Models;

class Article extends Eloquent {
    use \Famdirksen\Revisionable\RevisionableTrait;

    protected $revisionEnabled = true;
    protected $historyLimit = 500; //Stop tracking revisions after 500 changes have been made.
}

为了保持历史记录的限制,但如果你想要删除旧修订而不是停止跟踪修订,您可以通过设置$revisionCleanup来实现该功能。

namespace MyApp\Models;

class Article extends Eloquent {
    use \Famdirksen\Revisionable\RevisionableTrait;

    protected $revisionEnabled = true;
    protected $revisionCleanup = true; //Remove old revisions (works only when used with $historyLimit)
    protected $historyLimit = 500; //Maintain a maximum of 500 changes at any point of time, while cleaning up old revisions.
}

存储软删除

默认情况下,如果您的模型支持软删除,revisionable将存储此操作以及任何恢复,并将其作为模型的更新。

您可以通过将deleted_at添加到$dontKeepRevisionOf数组中来选择忽略删除和恢复。

为了更好地格式化deleted_at条目,您可以使用isEmpty格式化器(有关此示例,请参阅格式输出)。

存储创建

默认情况下,新模型的创建不会作为修订存储。只有对模型的后续更改会被存储。

如果您想将创建存储为修订,可以通过在模型中添加以下内容来覆盖此行为,将revisionCreationsEnabled设置为true:

protected $revisionCreationsEnabled = true;

更多控制

毫无疑问,有些情况下您可能只想为模型的某些字段存储修订历史记录,这以两种不同的方式支持。在您的模型中,您可以选择明确指定您希望跟踪的字段,其他所有字段将被忽略

protected $keepRevisionOf = [
    'title',
    ...
];

或者,您可以指定明确不希望跟踪的字段。其他所有字段将被跟踪。

protected $dontKeepRevisionOf = [
    'category_id',
    ...
];

$keepRevisionOf设置优先于$dontKeepRevisionOf

事件

!!!!不再使用!!!!

每次创建模型修订时都会触发一个事件。您可以监听revisionable.created
revisionable.savedrevisionable.deleted

// app/Providers/EventServiceProviders.php
public function boot(DispatcherContract $events)
{
    parent::boot($events);

    $events->listen('revisionable.*', function($model, $revisions) {
        // Do something with the revisions or the changed model. 
        dd($model, $revisions);
        ...
    });
}

格式输出

您可以使用eloquent访问器在模型中设置值输出,有关访问器的更多信息,请参阅laravel文档。以下文档已弃用

在您想要控制值输出格式的场景中,例如布尔字段,您可以在模型中设置$revisionFormattedFields数组。例如:

protected $revisionFormattedFields = [
    'title'  => 'string:<strong>%s</strong>',
    'public' => 'boolean:No|Yes',
    'modified' => 'datetime:m/d/Y g:i A',
    'deleted_at' => 'isEmpty:Active|Deleted',
    ...
];

您还可以使用模型中的$revisionFormattedFieldNames数组覆盖字段名称输出,例如:

protected $revisionFormattedFieldNames = [
    'title' => 'Title',
    'small_name' => 'Nickname',
    'deleted_at' => 'Deleted At',
    ...
];

这将在您使用$revision->fieldName()输出修订字段名称时发挥作用。

字符串

要格式化字符串,只需在值前加string:前缀,并确保包括%s(实际值将出现在格式化响应中),例如:

string:<strong>%s</strong>

布尔

布尔值默认显示为0或1,这相当平淡,对最终用户没有太多意义,因此可以使用此格式化器输出更美观的内容。在值前加boolean:前缀,然后添加您的false和true选项,用竖线分隔,例如:

boolean:No|Yes

日期时间

默认情况下,DateTime 将显示为 Y-m-d H:i:s。在值前加上 datetime:,然后添加您的日期时间格式,例如:

datetime:m/d/Y g:i A

是否为空

此功能依赖于布尔值,但它不是检查值是否为真或假,而是检查值是否为 null 或空字符串。

isEmpty:No|Yes

如果您想输出值,也可以接受 %s,以下示例将显示 'Nothing' 如果值为空,或者如果存在则显示实际值:

isEmpty:Nothing|%s

加载修订历史

要加载给定模型的修订历史,只需调用该模型的 revisionHistory 方法,例如:

$article = Article::find($id);
$history = $article->revisionHistory;

显示历史记录

大多数情况下,修订历史将包含足够的信息以直接输出变更历史,但在更新外键的情况下,我们需要进行一些映射并显示比 plan_id 从 3 更改为 1 更好的信息。

为此,有几个辅助方法可以显示更有洞察力的信息,因此您可以看到类似 Chris 将计划从青铜更改为黄金 的信息。

上面的结果将来自以下内容:

@foreach($account->revisionHistory as $history )
    <li>{{ $history->userResponsible()->first_name }} changed {{ $history->fieldName() }} from {{ $history->oldValue() }} to {{ $history->newValue() }}</li>
@endforeach

如果您还启用了创建的修订版,可以像这样显示:

@foreach($resource->revisionHistory as $history)
  @if($history->key == 'created_at' && !$history->old_value)
    <li>{{ $history->userResponsible()->first_name }} created this resource at {{ $history->newValue() }}</li>
  @else
    <li>{{ $history->userResponsible()->first_name }} changed {{ $history->fieldName() }} from {{ $history->oldValue() }} to {{ $history->newValue() }}</li>
  @endif
@endforeach

userResponsible()

返回负责制作修订的用户。返回用户模型,如果没有记录用户则返回 null。

加载的用户模型取决于您在 config/auth.php 文件中为 model 变量设置的值。

fieldName()

返回更新的字段名称,如果更新的字段是外键(在这个阶段,它只是查看字段是否有 _id 后缀),则返回 _id 之前的部分。例如,如果字段是 plan_id,则返回 plan

记住,您可以通过在模型中的 $revisionFormattedFieldNames 数组中覆盖字段名称的输出。

identifiableName()

当值(旧值或新值)是外键关系的 ID 时使用。

默认情况下,它简单地返回更新的模型的 ID。您需要在自己的模型中重写此方法以返回有意义的内容。例如:

use Famdirksen\Revisionable\Revisionable;

class Article extends Revisionable
{
    public function identifiableName()
    {
        return $this->title;
    }
}

oldValue() 和 newValue()

获取更新前后的模型值。如果是外键,则调用 identifiableName()。

作为修订的未知或无效外键

在旧值或新值为不再存在的外键或实际上是 null 的情况下,您可以在模型中设置两个变量来控制这些情况下的输出。

protected $revisionNullString = 'nothing';
protected $revisionUnknownString = 'unknown';

disableRevisionField()

有时临时禁用可修订字段可能很有用,如果您想要保存更新但不需要记录更改。

$object->disableRevisionField('title'); // Disables title

$object->disableRevisionField(['title', 'content']); // Disables title and content

贡献

鼓励和欢迎贡献;为了保持组织,所有错误和请求都应在主项目的 GitHub issues 标签下打开,在 famdirksen/revisionable/issues

所有拉取请求都应提交到 develop 分支,以便在合并到 master 分支之前进行测试。

遇到麻烦了吗?

如果您在使用此包时遇到问题,其他人可能已经遇到过相同的问题。您可以查看以下两个地方以找到您问题的常见答案:

如果您更愿意在 StackOverflow 上公开提问,请使用 'revisionable' 标签。