local-dynamics/laravel-revisionable

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

v11.0.4 2024-04-10 09:44 UTC

This package is auto-updated.

Last update: 2024-09-10 10:28:29 UTC


README

Laravel 5.8 Latest Version License

您是否希望为项目中的任何模型都保留修订历史,而不必为此做任何工作?通过简单地将 IsRevisionable 特性添加到您的模型中,您可以立即拥有它,并能够显示类似于以下的历史记录

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

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

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

安装

Revisionable可以通过composer安装,详细信息请见packagist,此处。

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

"local-dynamics/laravel-revisionable": "1.*",

运行composer update以下载包

php composer.phar update

打开config/app.php并注册所需的服务提供者

'providers' => [
    LocalDynamics\Revisionable\ServiceProvider::class,
]

发布配置和迁移

php artisan vendor:publish --provider="LocalDynamics\Revisionable\ServiceProvider"

最后,您还需要在包上运行迁移

php artisan migrate

文档

实现

对于任何您想要保留修订历史记录的模型,包含LocalDynamics\Revisionable命名空间,并在您的模型中使用IsRevisionable,例如:

namespace App;

use \LocalDynamics\Revisionable\Concerns\IsRevisionable;

class Article extends \Illuminate\Database\Eloquent\Model {

    use IsRevisionable;

}

实现说明

如果需要,您可以通过在模型中将$revisionEnabled设置为false来禁用修订。如果您想暂时禁用修订,或者如果您想创建一个继承自Revisionable的基模型,所有模型都继承自它,但您想为某些模型禁用Revisionable,这将很有用。

namespace App;

use \LocalDynamics\Revisionable\Concerns\IsRevisionable;

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

}

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

namespace App;

use \LocalDynamics\Revisionable\Concerns\IsRevisionable;

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

}

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

namespace App;

use \LocalDynamics\Revisionable\Concerns\IsRevisionable;

class Article extends \Illuminate\Database\Eloquent\Model {
    
    use IsRevisionable;
    
    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.createdrevisionable.savedrevisionable.deleted

// app/Providers/EventServiceProvider.php

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

    Event::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

日期时间

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

datetime:m/d/Y g:i A

是否为空

这是基于布尔值,但它不是检查 true 或 false 值,而是检查值是否为 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 LocalDynamics\Revisionable\Concerns\IsRevisionable;

class Article
{
    use IsRevisionable; // <- optional

    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

致谢