panoscape/history

Laravel 的 Eloquent 模型历史跟踪

v2.7.0 2024-03-22 03:52 UTC

This package is auto-updated.

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


README

Test Status Coverage Status Total Downloads Latest Stable Version License

历史

Laravel 的 Eloquent 模型历史跟踪

安装

Composer

Laravel 6.x 及以上版本

composer require panoscape/history

Laravel 5.6.x

composer require "panoscape/history:^1.0"

服务提供者和别名

仅适用于 Laravel 5.6.x

config/app.php

'providers' => [
    ...
    Panoscape\History\HistoryServiceProvider::class,
];
'aliases' => [
    ...
    'App\History' => Panoscape\History\History::class,
];

迁移

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=migrations

配置

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=config

本地化

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=translations

用法

HasOperations 特性添加到执行操作的模型。

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Panoscape\History\HasOperations;

class User extends Authenticatable
{
    use Notifiable, SoftDeletes, HasOperations;
}

HasHistories 特性添加到需要跟踪的模型。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Panoscape\History\HasHistories;

class Article extends Model
{
    use HasHistories;

    public function getModelLabel()
    {
        return $this->display_name;
    }
}

请注意,您需要实现特质中的抽象方法 getModelLabel。这为历史记录中模型实例的显示名称提供支持(如在 Who did what 中的 Who)。

获取模型的历史记录

$model->histories();
//or dynamic property
$model->histories;

获取用户的行为

$user->operations();
//or dynamic property
$user->operations;

附加查询条件

两者 historiesoperations 都返回 Eloquent 关联,也用作查询构建器。您可以通过链式条件添加进一步的约束。

// get the lastest 10 records
$model->histories()->orderBy('performed_at', 'desc')->take(10)

// filter by user id
$model->histories()->where('user_id', 10010)

历史

//get the associated model
$history->model();

//get the associated user
//the user is the authenticated user when the action is being performed
//it might be null if the history is performed unauthenticatedly
$history->user();
//check user existence
$history->hasUser();

//get the message
$history->message;

//get the meta(only available when it's an updating operation)
//the meta will be an array with the properties changing information
$history->meta;

//get the timestamp the action was performed at
$history->performed_at;

示例消息

Created Project my_project
   │       │         │
   │       │         └───── instance name(returned from `getModelLabel`)
   │       └─────────────── model name(class name or localized name)
   └─────────────────────── event name(default or localized name)

示例元数据

[
    ['key' => 'name', 'old' => 'myName', 'new' => 'myNewName'],
    ['key' => 'age', 'old' => 10, 'new' => 100],
    ...
]

自定义历史记录

除了内置的 created/updating/deleting/restoring 事件外,您还可以通过触发 ModelChanged 事件来跟踪自定义历史记录。

use Panoscape\History\Events\ModelChanged;

...
//fire a model changed event
event(new ModelChanged($user, 'User roles updated', $user->roles()->pluck('id')->toArray()));

ModelChanged 构造函数接受两个/三个/四个参数。第一个是与模型实例相关的;第二个是消息;第三个是可选的,是元数据(数组);第四个也是可选的,是事件的翻译键(见 本地化)。

本地化

您可以本地化模型类型名称。

为此,请将语言行添加到已发布语言文件中的 models 数组中,键为 类的蛇形基本名称

示例语言配置

/*
|--------------------------------------------------------------------------
| Tracker Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used across application for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/

'created' => '创建:model:label',

'updating' => 'actualizar :model :label',

'deleting' => ':model :label löschen',

'restored' => ':model:labelを復元',

//you may add your own model name language line here
'models' => [
    'project' => '项目',
    'component_template' => '组件模板',
    // 'model_base_name_in_snake_case' => 'translation',
]

这将翻译您的模型历史记录为

创建项目project_001

您还可以从 ModelChanged 事件翻译自定义历史消息

/*
|--------------------------------------------------------------------------
| Tracker Language Lines
|--------------------------------------------------------------------------
*/
'switched_role' => ':model switched role',
// if you specified the translation key, the message argument will be ignored, simply just pass `null`
event(new ModelChanged($user, null, $user->roles()->pluck('id')->toArray()), 'switched_role');

过滤器

您可以在配置文件中设置白名单和黑名单。请参阅已发布配置文件中的描述指南。

/*
|--------------------------------------------------------------
| Events whitelist
|--------------------------------------------------------------
|
| Events in this array will be recorded.
| Available events are: created, updating, deleting, restored
|
*/
'events_whitelist' => [
    'created', 'updating', 'deleting', 'restored',
],

/*
|--------------------------------------------------------------
| Attributes blacklist
|--------------------------------------------------------------
| 
| Please add the whole class names. Example: \App\User:class
| For each model, attributes in its respect array will NOT be recorded into meta when performing update operation.
|
*/
'attributes_blacklist' => [
    // \App\User::class => [
    //     'password'
    // ],
],

/*
|--------------------------------------------------------------
| User type blacklist
|--------------------------------------------------------------
|
| Operations performed by user types in this array will NOT be recorded.
| Please add the whole class names. Example: \App\Admin:class
| Use 'nobody' to bypass unauthenticated operations
|
*/
'user_blacklist' => [
    // \App\Admin:class,
    // 'nobody'
],
/*
|--------------------------------------------------------------
| Enviroments blacklist
|--------------------------------------------------------------
|
| When application's environment is in the list, tracker will be disabled
|
*/
'env_blacklist' => [
    // 'test'
],

身份验证守卫

如果您的用户使用非默认身份验证守卫,即使历史记录来源是由已验证用户生成的,您也可能会看到所有 $history->hasUser() 都变为 false

要修复此问题,您需要在配置文件中启用自定义身份验证守卫扫描。

/*
|--------------------------------------------------------------
| Enable auth guards scanning
|--------------------------------------------------------------
|
| You only need to enable this if your users are using non-default auth guards.
| In that case, all tracked user operations will be anonymous.
|
| - Set to `true` to use a full scan mode: all auth guards will be checked. However this does not ensure guard priority.
| - Set to an array to scan only specific auth guards(in the given order). e.g. `['web', 'api', 'admin']`
|
*/
'auth_guards' => null

自定义元数据

您可以定义自己的方法来定义元数据。默认情况下,对于 updating 事件,元数据由修改的键组成,对于其他事件,元数据为 null

只需为特质重新定义方法 getModelMeta

示例

class Article extends Model
{
    // if you want to use default trait method, you need to redeclare it with a new name
    use HasHistories {
        getModelMeta as protected traitGetModelMeta;
    };

    ...
    
    public function getModelMeta($event)
    {
        // using defaults for updating
        if($event == 'updating') return $this->traitGetModelMeta($event);
        // passing full model to meta
        // ['key1' => 'value1', 'key2' => 'value2', ...]
        else return $this;
    }
}

已知问题

  1. 在更新模型时,如果其模型标签(从 getModelLabel 返回的属性)已更改,则历史记录消息将使用其新属性,这可能不是您期望的结果。
class Article extends Model
{
    use HasHistories;

    public function getModelLabel()
    {
        return $this->title;
    }
}
// original title is 'my title'
// modify title
$article->title = 'new title';
$article->save();
// the updating history message
// expect: Updating Article my title
// actual: Updating Article new title

一种解决方案

public function getModelLabel()
{
    return $this->getOriginal('title', $this->title);
}