penobit / revisionable
无需思考即可为您的模型保留修订历史,作为一个用于Laravel的包创建
Requires
- php: >=7.3.0
- illuminate/support: ~4.0|~5.0|~5.1|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0
- laravel/framework: ~5.4|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0
Requires (Dev)
- orchestra/testbench: ~3.0|^8.0
README
基于 VentureCraft/Revisionable
不是很好吗?在项目中为任何模型保留修订历史,无需做任何工作。只需将 HasRevisions
特性添加到您的模型中,您就可以立即实现这一点,并且能够显示类似以下的历史记录
- Chris 将标题从 'Something' 更改为 'Something else'
- Chris 将类别从 'News' 更改为 'Breaking news'
- Matt 将类别从 'Breaking news' 更改为 'News'
因此,您不仅可以查看发生了什么历史,还可以知道是谁做了什么,因此有责任。
Revisionable 是一个 Laravel 包,允许您无需思考即可为模型保留修订历史。有关背景和更多信息,请参阅这篇文章
与第三方 Auth / Eloquent 扩展一起使用
Revisionable 支持 Auth,由以下提供
(推荐) Revisionable 现在也可以用作 特性,因此您的模型可以继续扩展 Eloquent,或者任何扩展 Eloquent 的类(例如 Ardent)。
安装
Revisionable 可通过 composer 安装,详细信息请见 packagist,此处。
将以下内容添加到您的项目的 composer.json 文件的 require
部分
"penobit/revisionable": "1.*",
运行 composer update 下载包
php composer.phar update
打开 config/app.php 并注册所需的服务提供者(Laravel 5.x)
'providers' => [
Penobit\Revisionable\ServiceProvider::class,
]
发布配置和迁移(Laravel 5.x)
php artisan vendor:publish --provider="Penobit\Revisionable\ServiceProvider"
最后,您还需要在包上运行迁移(Laravel 5.x)
php artisan migrate
对于 Laravel 4.x 用户
php artisan migrate --package=penobit/revisionable
如果您将频繁进行完全的迁移上下文(使用
migrate:refresh
),您可以做的另一件事是将迁移文件从包复制到您的app/database
文件夹,并将类名从CreateRevisionsTable
更改为类似CreateRevisionTable
的内容(不带 's',否则您将收到一个错误,表示存在重复的类)
cp vendor/penobit/revisionable/src/migrations/2013_04_09_062329_create_revisions_table.php database/migrations/
文档
实现
新的基于特性的实现(推荐)
特性需要 PHP >= 5.4
对于您想要保留修订历史记录的任何模型,请包括 Penobit\Revisionable
命名空间并在您的模型中使用 HasRevisions
,例如
namespace App; use \Penobit\Revisionable\HasRevisions; class Article extends \Illuminate\Database\Eloquent\Model { use HasRevisions; }
作为一个特性,Revisionable 现在可以与标准的 Eloquent 模型一起使用,或者任何扩展 Eloquent 的类(例如 Ardent)。
基于旧类的方法
新的基于特性的方法与现有的 Revisionable 安装向后兼容。您仍然可以使用以下安装说明,这本质上是一个用于特性的包装器。
对于您想要保留修订历史记录的任何模型,请包括 Penobit\Revisionable
命名空间并在您的模型中使用 HasRevisions
,例如
use Penobit\Revisionable\Revisionable; namespace App; class Article extends Revisionable { }
注意:这也适用于命名空间模型。
实现说明
如有需要,您可以通过将您的模型中的$revisionEnabled
设置为false来禁用修订版本控制。这可以在您想暂时禁用修订版本控制或想创建一个继承自Revisionable的自定义基础模型时很有用,该模型扩展了您的所有模型,但您希望某些模型不使用Revisionable。
namespace App; use \Penobit\Revisionable\HasRevisions; class Article extends \Illuminate\Database\Eloquent\Model { protected $revisionEnabled = false; }
您还可以在进行了X次修订后通过将$historyLimit
设置为想要保留的修订版本数量来停止修订版本控制。
namespace App; use \Penobit\Revisionable\HasRevisions; 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 \Penobit\Revisionable\HasRevisions; 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.saved
或revisionable.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
加载修订历史记录
要加载特定模型的修订历史,只需在该模型上调用 revisions
方法,例如:
$article = Article::find($id); $history = $article->revisions;
显示历史记录
大部分情况下,修订历史将包含足够的信息以直接输出更改历史,但在外键更新的情况下,我们需要能够进行一些映射,并显示比 plan_id changed from 3 to 1
更美观的内容。
为此,有一些辅助方法可以帮助显示更深入的信息,因此可以显示类似 Chris changed plan from bronze to gold
的内容。
上述内容将是以下内容的输出:
@foreach($account->revisions as $history ) <li>{{ $history->user()->first_name }} changed {{ $history->fieldName() }} from {{ $history->oldValue() }} to {{ $history->newValue() }}</li> @endforeach
如果您已启用创建的修订,则可以显示如下:
@foreach($resource->revisions as $history) @if($history->key == 'created_at' && !$history->old_value) <li>{{ $history->user()->first_name }} created this resource at {{ $history->newValue() }}</li> @else <li>{{ $history->user()->first_name }} changed {{ $history->fieldName() }} from {{ $history->oldValue() }} to {{ $history->newValue() }}</li> @endif @endforeach
user()
返回负责进行修订的用户。返回用户模型,如果没有记录用户则返回 false。
加载的用户模型取决于您在 config/auth.php
文件中为 model
变量设置的配置。
fieldName()
返回已更新的字段的名称。如果更新的字段是外键(在此阶段,它仅检查字段是否具有 _id
后缀),则返回 _id
之前的文本。例如,如果字段是 plan_id
,则返回 plan
。
记住,您可以使用模型中的
$revisionFormattedFieldNames
数组覆盖字段的输出。
identifiableName()
当值(旧值或新值)是外键关系 ID 时使用。
默认情况下,它简单地返回更新的模型 ID。您可以根据自己的模型重写此方法以返回有意义的值。例如:
use Penobit\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 标签页上,位于 penobit/revisionable/issues
所有拉取请求都应该提交到develop分支,以便在合并到master分支之前进行测试。
遇到麻烦了吗?
如果您在使用此软件包时遇到问题,很可能其他人已经遇到过相同的问题。您可以通过以下两个地方查找常见问题的答案:
如果您更喜欢在StackOverflow上公开提问,请使用'tag'标签。