Code Monkey home page Code Monkey logo

history's Introduction

Test Status Coverage Status Total Downloads Latest Stable Version License

History

Eloquent model history tracking for Laravel

Installation

Composer

Laravel 6.x and above

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' => [
    ...
    'App\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/restoring 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
| 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'
],

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

history's People

Contributors

adbhutagaurangadas avatar fcollombon avatar jjjrmy avatar kirepetrov avatar laravel-shift avatar mateuskurten avatar nxmndr avatar seancheung avatar timopaul avatar vincenzoraco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

history's Issues

boolean and timestamp(carbon)

Could you please fix it

[{"key":"pre_order","old":0,"new":false}]

[{"key":"shipp_at","old":"2021-05-31T21:00:00.000000Z","new":"2021-05-31T21:00:00.000000Z"}]

primary key

What happens when the primary key is composed?

User::class to getMorphClass()

Hello!

It is correct to use 'getMorphClass' with an alias on the 'user_type' field of the History model, rather than directly specifying the '\App\User' class name.

return static::getAuth()->check() ? get_class(static::getAuth()->user()) : null;

I suggest
return static::getAuth()->check() ? static::getAuth()->user()->getMorphClass() : null;

Of course, some changes will be required in other files as well.

Thank you for your attention.

HistoryOserver isIgnored blacklist test

When testing the existence of the model in the blacklist,

panoscape/history/src/HistoryObserver.php, line 114;

the index name ($array = $blacklist [$name];) does not exist during an updating event.

So I change this statement to $array = isset($blacklist [$name]) ? $blacklist[$name] : [];

Relationships touch

Does this package support relationships? I have some tasks and I want to track when relationships are added as well.
Would I have to create custom history triggers for that to work or is there a way using $touches?

Release 1.0.1

hey, can you create a new release for with the last changes please? i would like to use it in my running projects and make update with the composer.

Cannot Install

vagrant@homestead:~/liveleads$ composer require panoscape/history
1/3: http://repo.packagist.org/p/provider-latest$7f656255e7b2a5283b4f27a1f319e64352efcbf29ab8d197e8ca2f18cd7d92c6.json
2/3: http://repo.packagist.org/p/provider-2020-04$6b12b12e0eb343a930029e1aad1525f979721416cf17744b20fcc4ccbd2342b4.json
3/3: http://repo.packagist.org/p/provider-2020-01$ae3a8c17fbb9e4c9ae542510759e90db46063180e5910612980d38d8c13258cc.json
Finished: success: 3, skipped: 0, failure: 0, total: 3 Using version ^1.0 for panoscape/history
./composer.json has been updated
Loading composer repositories with package information Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- Conclusion: don't install laravel/framework v7.11.0
- Conclusion: don't install laravel/framework 7.x-dev
- Conclusion: remove dropoutventures/laravel-address-validation dev-master
- Conclusion: don't install dropoutventures/laravel-address-validation dev-master
- Installation request for laravel/framework (locked at v7.9.2, required as ^7.0) -> satisfiable by laravel/framework[v7.9.2].
- Installation request for dropoutventures/laravel-address-validation dev-master -> satisfiable by dropoutventures/laravel-address-validation[dev-master].
- Conclusion: don't install illuminate/support v7.11.0
- dropoutventures/laravel-address-validation dev-master requires laravel/framework ^7.0 -> satisfiable by laravel/framework[v7.9.2, 7.x-dev, v7.0.0, v7.0.1, v7.0.2, v7.0.3, v7.0.4, v7.0.5, v7.0.6, v7.0.7, v7.0.8, v7.1.0, v7.1.1, v7.1.2, v7.1.3, v7.10.0, v7.10.1, v7.10.2, v7.10.3, v7.11.0, v7.2.0, v7.2.1, v7.2.2, v7.3.0, v7.4.0, v7.5.0, v7.5.1, v7.5.2, v7.6.0, v7.6.1, v7.6.2, v7.7.0, v7.7.1, v7.8.0, v7.8.1, v7.9.0, v7.9.1].
- don't install laravel/framework v7.0.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.3|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.4|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.5|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.6|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.7|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.0.8|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.1.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.1.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.1.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.1.3|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.10.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.10.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.10.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.10.3|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.2.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.2.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.2.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.3.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.4.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.5.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.5.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.5.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.6.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.6.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.6.2|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.7.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.7.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.8.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.8.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.9.0|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.9.1|don't install illuminate/support 7.x-dev
- don't install laravel/framework v7.9.2|don't install illuminate/support 7.x-dev
- don't install illuminate/support 7.x-dev|remove laravel/framework v7.9.2
- don't install illuminate/support 7.x-dev|don't install laravel/framework v7.9.2
- Installation request for illuminate/support ^7.11 -> satisfiable by illuminate/support[7.x-dev, v7.11.0], laravel/framework[7.x-dev, v7.11.0].

Installation failed, reverting ./composer.json to its original content.

Histories package does not Work Please help

I am using Macos Mojave
-- PHP 8.0.7
-- Laravel Framework 8.46.0

$post = Post::factory()->create();

    $post->title = 'Hello world';
    $post->save();

    dd($post->histories);

returns empty array
expect result is the past and current data of the model, kindly tell me if i am wrong somewhere

Sorting model history

Hi! I like this extension in my project, you doing great job, thank you.

Please, help me finding a simple way to set asc/desc ordering by "performed_at" attribute on history rendering?

getModelName() should be customizable

getModelName() function should be customizable or available in trait itself so it can be redeclared in models, because model names are not always suitable to display in messages

Skip empty time

Is it possible to ignore certain changes? Eg:
[{"key":"published_at","old":"2022-11-10 00:00:00","new":"2022-11-10"}]

Make the guards of auth configurable

the guard of backend admin is not the default one for front user ,they share the common code
so

public static function getUserID()
{
return auth()->check() ? auth()->user()->id : null;
}
only get the default guard for front user, make it not working for the users of backend admin

changing user type

Hi, is there a place where I can change the user type, by default is 'App/User' so I just want to change it

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.