定制 panoscape/history 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

panoscape/history

Composer 安装命令:

composer require panoscape/history

包简介

Eloquent model history tracking for Laravel

README 文档

README

Test Status Coverage Status Total Downloads Latest Stable Version License

History

Eloquent model history tracking for Laravel

Installation

Composer

Laravel 6.x and above (tested up to Laravel 13.x)

composer require panoscape/history

Laravel 5.6.x

composer require "panoscape/history:^1.0"

Service provider and alias

Only required for Laravel 5.6.x

config/app.php

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

Migration

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

Config

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

Localization

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

Usage

Add HasOperations trait to user model that performs operations.

<?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;
}

Add HasHistories trait to the model that will be tracked.

<?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;
    }
}

Remember that you'll need to implement the abstract getModelLabel method from the trait. This provides the model instance's display name in histories (as Who in Who did what).

Get histories of a model

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

Get operations of a user

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

Additional query conditions

Both histories and operations return Eloquent relationships which also serve as query builders. You can add further constraints by chaining conditions:

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

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

History

//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;

Example message

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

Example meta

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

Custom history

Besides the built in created/updating/deleting/restored events, you may track custom history record by firing an ModelChanged event.

use Panoscape\History\Events\ModelChanged;

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

The ModelChanged constructor accepts two/three/four arguments. The first is the associated model instance; the second is the message; the third is optional, which is the meta(array); the fourth is also optional, being the translation key of the event(see Localization).

Localization

You may localize the model's type name.

To do that, add the language line to the models array in the published language file, with the key being the class's base name in snake case.

Example language config

/*
|--------------------------------------------------------------------------
| 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',
]

This will translate your model history into

创建项目project_001

You can also translate custom history messages from ModelChanged events

/*
|--------------------------------------------------------------------------
| 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'));

Filters

You may set whitelist and blacklist in config file. Please follow the description guide in the published config file.

/*
|--------------------------------------------------------------
| 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
| Morph map aliases are also accepted. Example: 'admin'
| 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'
],

Toggle tracking

Tracking can be switched on or off globally and per runtime in the config file.

// Master switch. Set to false to disable all tracking.
'enabled' => true,

// By default histories are NOT recorded when the application runs in console
// (artisan commands, seeders, queue workers). Set to true to record them.
'console_enabled' => false,

// By default histories are NOT recorded while running tests. Set to true to record them.
'test_enabled' => false,

Auth guards

If your users are using non-default auth guards, you might see all $history->hasUser() become false even though the history sources were generated by authenticated users.

To fix this, you'll need to enable custom auth guards scanning in config file:

/*
|--------------------------------------------------------------
| 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

Custom meta

You can define your own method for meta data. By default for updating event meta consists of modified keys and for other events meta is null.

Just redefine the method getModelMeta for the trait.

Example:

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;
    }
}

Known issues

  1. When updating a model, if its model label(attributes returned from getModelLabel) has been modified, the history message will use its new attributes, which might not be what you expect.
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

A workaround

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

panoscape/history 适用场景与选型建议

panoscape/history 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 136.35k 次下载、GitHub Stars 达 164, 最近一次更新时间为 2016 年 11 月 22 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「log」 「model」 「laravel」 「eloquent」 「tracking」 「history」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 panoscape/history 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 panoscape/history 我们能提供哪些服务?
定制开发 / 二次开发

基于 panoscape/history 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 136.35k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 164
  • 点击次数: 27
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 164
  • Watchers: 4
  • Forks: 26
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2016-11-22