imagina/revisionable

无需思考即可为您的模型保存版本历史,作为Laravel的包使用

dev-master 2022-08-30 14:13 UTC

This package is not auto-updated.

Last update: 2024-09-25 22:55:18 UTC


README

Revisionable for Laravel

Latest Version Downloads License

您是否希望在项目中为任何模型都保存版本历史,而不必为此做任何工作。通过简单地将 RevisionableTrait 特性添加到您的模型中,您可以立即获得这样的功能,并能够显示类似于以下的历史记录

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

因此,您不仅可以看到发生了什么,还可以看到是谁做了什么,因此有责任追究。

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

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

Revisionable 支持 Sentry by Cartalyst 驱动的 Auth

(推荐) Revisionable 现在也可以作为一个 特性 使用,因此您的模型可以继续扩展 Eloquent,或者任何其他扩展 Eloquent 的类(例如 Ardent)。

安装

Revisionable 可以通过 composer 安装,详细信息在 packagist 上。

将以下内容添加到您项目 composer.json 文件的 require 部分

"venturecraft/revisionable": "1.*",

运行 composer update 下载包

php composer.phar update

打开 config/app.php 并注册所需的服务提供程序(Laravel 5.x)

'providers' => [
	Venturecraft\Revisionable\RevisionableServiceProvider::class,
]

发布配置和迁移(Laravel 5.x)

php artisan vendor:publish --provider="Venturecraft\Revisionable\RevisionableServiceProvider"

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

php artisan migrate

针对 Laravel 4.x 用户

php artisan migrate --package=venturecraft/revisionable

如果您将频繁地进行完全的迁移上下文(使用 migrate:refresh),您可以采取的另一件事是将迁移文件从包复制到您的 app/database 文件夹,并将类名从 CreateRevisionsTable 改为类似 CreateRevisionTable 的名称(不带 's',否则您将收到一个错误消息,表明有重复的类)

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

文档

实现

新的基于特性的实现(推荐)

特性需要 PHP >= 5.4

对于您想要保存版本历史的任何模型,请包含 VentureCraft\Revisionable 命名空间,并在您的模型中使用 RevisionableTrait,例如:

namespace App;

use \Venturecraft\Revisionable\RevisionableTrait;

class Article extends \Illuminate\Database\Eloquent\Model {
    use RevisionableTrait;
}

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

基于旧类的方法

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

对于您想要保存版本历史的任何模型,请包含 VentureCraft\Revisionable 命名空间,并在您的模型中使用 RevisionableTrait,例如:

use Venturecraft\Revisionable\Revisionable;

namespace App;

class Article extends Revisionable { }

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

实现说明

如果需要,您可以在模型中将 $revisionEnabled 设置为 false 以禁用修订版本。如果您想暂时禁用修订版本,或者想创建一个继承自 Revisionable 的基础模型,该基础模型被所有模型继承,但您想关闭某些模型的 Revisionable,这可能会很有用。

namespace App;

use \Venturecraft\Revisionable\RevisionableTrait;

class Article extends \Illuminate\Database\Eloquent\Model {
    protected $revisionEnabled = false;
}

您还可以在进行了 X 多次修订之后禁用修订版本,通过将 $historyLimit 设置为您想要在停止修订之前保留的修订次数。

namespace App;

use \Venturecraft\Revisionable\RevisionableTrait;

class Article extends \Illuminate\Database\Eloquent\Model {
    protected $revisionEnabled = true;
    protected $historyLimit = 500; //Stop tracking revisions after 500 changes have been made.
}

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

namespace App;

use \Venturecraft\Revisionable\RevisionableTrait;

class Article extends \Illuminate\Database\Eloquent\Model {
    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 格式化器(有关此示例,请参阅格式化输出)。

存储强制删除

默认情况下,模型的强制删除不会作为修订版本存储。

如果您想将强制删除作为修订版本存储,可以通过在模型中添加以下内容来覆盖此行为,将 revisionForceDeleteEnabled 设置为 true

protected $revisionForceDeleteEnabled = true;

在这种情况下,created_at 字段将作为键存储,其中 oldValue() 的值等于模型创建日期,而 newValue() 的值等于 null

注意!请谨慎开启此设置!由于模型已保存在修订中,现在不存在,因此您将无法获取其对象或其关系。

存储创建

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

如果您想将创建作为修订版本存储,可以通过将 revisionCreationsEnabled 设置为 true 来覆盖此行为,如下添加到您的模型中

protected $revisionCreationsEnabled = true;

更多控制

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

protected $keepRevisionOf = ['title'];

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

protected $dontKeepRevisionOf = ['category_id'];

$keepRevisionOf 设置优先于 $dontKeepRevisionOf

在修订版本中存储附加字段

在某些情况下,您可能希望在每个修订版本中存储模型的附加元数据。一个例子可能是如果您必须跟踪账户以及用户。只需创建自己的新迁移来添加您想要添加到修订模型中的字段,然后将它们添加到您的 config/revisionable.php 中,如下所示

'additional_fields' => ['account_id', 'permissions_id', 'other_id'], 

如果该列存在于模型中,它将包含在修订中。

确保如果您不能保证每个模型中都有该列,您应在迁移中将该列设置为 nullable()

事件

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

// app/Providers/EventServiceProvider.php

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

    $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

选项

类似于 "布尔值",任何文本或数值都可以作为源值(通常标志存储在数据库中)。该格式允许您根据值指定不同的输出。将其视为关联数组,其中键与值由点分隔。数组元素由竖线分隔。

options: search.On the search|network.In networks

日期时间

默认情况下,日期时间将显示为 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 Venturecraft\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的主要项目问题标签页中打开,网址为venturecraft/revisionable/issues

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

遇到麻烦了吗?

如果您在使用此包时遇到问题,很可能会有人遇到过相同的问题。您可以查找以下两个地方以获取对您问题的常见答案:

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