gazugafan/laravel-changelog

为 Laravel 5 记录数据库变更日志

v1.0.0 2017-02-18 07:02 UTC

This package is auto-updated.

Last update: 2024-09-21 22:05:04 UTC


README

为 Laravel 5 记录数据库变更日志

自动记录谁做了什么,何时,从哪里。与 laravel-temporal 配合使用效果更佳!

要求

  • 此软件仅在 Laravel 5.4 和 PHP 7.1 上进行过测试。如果您发现它在旧版本上也能工作,请告诉我!
  • 此外,也仅在 MySQL/MariaDB 上进行了测试。如果您发现它可以与其他数据库一起使用,请告诉我!
  • 尚未编写单元测试。

安装

通过 Composer 安装...

composer require gazugafan/laravel-changelog

将服务提供者添加到 config/app.php 文件中的 providers 数组...

'providers' => [
	...
	\Gazugafan\Changelog\ServiceProvider::class
];

并在 config/app.php 文件中的 aliases 数组中添加一个别名...

'aliases' => [
	...
	'Change' => Gazugafan\Changelog\Facades\Change::class
];

概述

变更记录表将获得一个新的 change_id 列,它与一个新创建的 changes 表相关联,用于保存关于记录最新变更的详细信息。将变更包裹在数据库事务中可以使多个表受到同一变更的影响。每次变更时(如果可能),都会自动记录认证用户的 ID,并且您可以包括像备注和变更发生的界面这样的详细信息。

laravel-temporal 配合使用时,这将为您提供对记录的每次变更的历史记录,包括谁进行了每次变更以及确切更改了什么。

模式迁移

运行以下迁移来创建必要的 changes 表...

Schema::create('changes', function (Blueprint $table) {
	$table->increments('id');
	$table->timestamps();
	$table->unsignedInteger('user_id')->nullable();
	$table->string('interface', 127)->nullable();
	$table->string('notes', 255)->nullable();
	$table->enum('status', ['pending', 'complete', 'failed'])->default('pending');
	$table->index('status');
});

您还需要将 change_id 列添加到您想要记录变更的任何表中...

Schema::table('widgets', function (Blueprint $table) {
	$table->unsignedInteger('change_id')->nullable();
});

模型设置

要使您的模型支持变更记录,只需将 Gazugafan\Changelog\Changelog 特性添加到模型的类...

class Widget extends Model
{
	use Changelog; //add all the changelog features
}

您还可以设置一些模型特定的选项...

class Widget extends Model
{
	use Changelog; //add all the changelog features

	protected $forceChangelogging = true; //set to false to allow saving outside of changes
	protected $changeIDColumn = 'change_id'; //in case you want to use a different column for some reason
}

用法

记录变更

要开始记录变更,调用 Change::begin()。您可以可选地指定与变更一起记录的界面和备注...

Change::begin('Web API', 'Painting a widget red');

这将向 changes 表中插入一个新的变更记录,状态为 pending,并自动填写可用的认证用户的 ID。接下来,开始进行与您的变更相关的一切操作...

$widget = \App\Widget::find(123);
$widget->color = 'red';
$widget->save();

$redPaint = \App\Paint::where('color', 'red')->first();
$redPaint->available -= 1;
$redPaint->save();

每次您使用具有 Changelog 特性的模型调用 save() 时,正在进行的变更的 ID 将自动填写到记录的 change_id 中。

当您完成您的变更时,调用 Change::commit()。这将通过将状态更新为 complete 来最终确定变更。如果变更由于某些原因失败,您可以通过调用 Change::rollBack() 来放弃变更并将其状态设置为 failed。以下是一个示例...

try {
	Change::begin('Web API', 'Painting a widget red');
	
	$widget = \App\Widget::find(123);
	$widget->color = 'red';
	$widget->save();
	
	$redPaint = \App\Paint::where('color', 'red')->first();
	$redPaint->available -= 1;
	$redPaint->save();
	
	Change::commit();
} catch (Exception $e) {
	Change::rollBack();
}

如果这看起来类似于 Laravel 中处理数据库事务的方式,那不是巧合!默认情况下,上述 Change 方法也会将您的变更包裹在事务中。这意味着如果在您的变更过程中发生错误,整个操作将自动回滚。换句话说:要么整个变更成功,要么整个操作失败。没有部分变更可以成功完成。同样,就像 Laravel 事务一样,您可以使用闭包使用 transaction 方法...

Change::transaction(function (){
	$widget = \App\Widget::find(123);
	$widget->color = 'red';
	$widget->save();
	
	$redPaint = \App\Paint::where('color', 'red')->first();
	$redPaint->available -= 1;
	$redPaint->save();
}, 'Web API', 'Painting a widget red', 5); //make 5 attempts in case of deadlock

如果您出于某种原因希望禁用事务的使用,请将 false 作为第三个参数传递给 Change::begin()...

try {
	Change::begin('Web API', 'Painting a widget red', FALSE); //start a change without transactions
	
	$widget = \App\Widget::find(123);
	$widget->color = 'red';
	$widget->save();
	
	$redPaint = \App\Paint::where('color', 'derek')->first();
	$redPaint->available -= 1; //there's no "derek" color, dummy! This is gonna blow up!
	$redPaint->save();
	
	Change::commit();
} catch (Exception $e) {
	//great, now we've somehow painted a widget red without using any red paint.
	//if only we had used transactions, we'd rollback painting the widget red here...
	Change::rollBack(); //but this will still log the change's status as "failed", at least
}

通过 Auth::id() 获取已认证用户的ID。如果您想用自己定义的行为覆盖它,可以向 Change::authID() 传递一个闭包...

Change::authID(function(){
	return getMySpecialLoggedInUsersID();
});
Change::begin();
...

陷阱

  • 与正常的Laravel数据库事务不同,嵌套更改不受支持。如果在另一个更改正在进行时尝试启动一个更改,将导致错误。